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,258 +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 openai_1 = __importDefault(require("openai"));
7
- const OpenAIClientAdapter_1 = require("./OpenAIClientAdapter");
8
- const types_1 = require("./types");
9
- // Mock the entire 'openai' module
10
- jest.mock('openai');
11
- // Cast the mocked module to allow setting up mock implementations
12
- const MockOpenAI = openai_1.default;
13
- describe('OpenAIClientAdapter', () => {
14
- let adapter;
15
- let mockCreate;
16
- let basicRequest;
17
- beforeEach(() => {
18
- // Reset mocks before each test
19
- MockOpenAI.mockClear();
20
- mockCreate = jest.fn();
21
- // Mock the chat.completions.create method
22
- MockOpenAI.prototype.chat = {
23
- completions: {
24
- create: mockCreate,
25
- },
26
- };
27
- adapter = new OpenAIClientAdapter_1.OpenAIClientAdapter();
28
- basicRequest = {
29
- providerId: 'openai',
30
- modelId: 'gpt-4.1',
31
- messages: [{ role: 'user', content: 'Hello' }],
32
- settings: {
33
- temperature: 0.7,
34
- maxTokens: 100,
35
- topP: 1,
36
- frequencyPenalty: 0,
37
- presencePenalty: 0,
38
- stopSequences: [],
39
- user: 'test-user',
40
- geminiSafetySettings: [],
41
- supportsSystemMessage: true,
42
- reasoning: {
43
- enabled: false,
44
- effort: undefined,
45
- maxTokens: undefined,
46
- exclude: false
47
- },
48
- thinkingExtraction: {
49
- enabled: true,
50
- tag: 'thinking'
51
- }
52
- }
53
- };
54
- });
55
- describe('sendMessage', () => {
56
- it('should format the request correctly and call the OpenAI API', async () => {
57
- // Setup mock response
58
- mockCreate.mockResolvedValueOnce({
59
- id: 'chatcmpl-123',
60
- object: 'chat.completion',
61
- created: 1234567890,
62
- model: 'gpt-4.1',
63
- choices: [{
64
- index: 0,
65
- message: {
66
- role: 'assistant',
67
- content: 'Hello! How can I help you today?'
68
- },
69
- finish_reason: 'stop'
70
- }],
71
- usage: {
72
- prompt_tokens: 10,
73
- completion_tokens: 20,
74
- total_tokens: 30
75
- }
76
- });
77
- const response = await adapter.sendMessage(basicRequest, 'test-api-key');
78
- // Verify OpenAI was instantiated with the API key
79
- expect(MockOpenAI).toHaveBeenCalledWith({
80
- apiKey: 'test-api-key',
81
- baseURL: undefined
82
- });
83
- // Verify the create method was called with correct parameters
84
- expect(mockCreate).toHaveBeenCalledWith({
85
- model: 'gpt-4.1',
86
- messages: [{ role: 'user', content: 'Hello' }],
87
- temperature: 0.7,
88
- max_completion_tokens: 100,
89
- top_p: 1,
90
- user: 'test-user'
91
- });
92
- // Verify the response
93
- expect(response.object).toBe('chat.completion');
94
- const successResponse = response;
95
- expect(successResponse.id).toBe('chatcmpl-123');
96
- expect(successResponse.provider).toBe('openai');
97
- expect(successResponse.model).toBe('gpt-4.1');
98
- expect(successResponse.choices[0].message.content).toBe('Hello! How can I help you today?');
99
- expect(successResponse.usage?.total_tokens).toBe(30);
100
- });
101
- it('should handle system messages correctly', async () => {
102
- basicRequest.messages = [
103
- { role: 'system', content: 'You are a helpful assistant.' },
104
- { role: 'user', content: 'Hello' }
105
- ];
106
- mockCreate.mockResolvedValueOnce({
107
- id: 'chatcmpl-123',
108
- object: 'chat.completion',
109
- created: 1234567890,
110
- model: 'gpt-4.1',
111
- choices: [{
112
- index: 0,
113
- message: { role: 'assistant', content: 'Hello!' },
114
- finish_reason: 'stop'
115
- }]
116
- });
117
- await adapter.sendMessage(basicRequest, 'test-api-key');
118
- expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({
119
- messages: [
120
- { role: 'system', content: 'You are a helpful assistant.' },
121
- { role: 'user', content: 'Hello' }
122
- ]
123
- }));
124
- });
125
- it('should handle stop sequences correctly', async () => {
126
- basicRequest.settings.stopSequences = ['END', 'STOP'];
127
- mockCreate.mockResolvedValueOnce({
128
- id: 'chatcmpl-123',
129
- object: 'chat.completion',
130
- created: 1234567890,
131
- model: 'gpt-4.1',
132
- choices: [{
133
- index: 0,
134
- message: { role: 'assistant', content: 'Response' },
135
- finish_reason: 'stop'
136
- }]
137
- });
138
- await adapter.sendMessage(basicRequest, 'test-api-key');
139
- expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({
140
- stop: ['END', 'STOP']
141
- }));
142
- });
143
- it('should use custom baseURL when provided', async () => {
144
- const customAdapter = new OpenAIClientAdapter_1.OpenAIClientAdapter({ baseURL: 'https://custom.api.com' });
145
- mockCreate.mockResolvedValueOnce({
146
- id: 'chatcmpl-123',
147
- object: 'chat.completion',
148
- created: 1234567890,
149
- model: 'gpt-4.1',
150
- choices: [{
151
- index: 0,
152
- message: { role: 'assistant', content: 'Response' },
153
- finish_reason: 'stop'
154
- }]
155
- });
156
- await customAdapter.sendMessage(basicRequest, 'test-api-key');
157
- expect(MockOpenAI).toHaveBeenCalledWith({
158
- apiKey: 'test-api-key',
159
- baseURL: 'https://custom.api.com'
160
- });
161
- });
162
- describe('error handling', () => {
163
- it('should handle authentication errors (401)', async () => {
164
- const apiError = new Error('Invalid API key');
165
- apiError.status = 401;
166
- mockCreate.mockRejectedValueOnce(apiError);
167
- const response = await adapter.sendMessage(basicRequest, 'invalid-key');
168
- expect(response.object).toBe('error');
169
- const errorResponse = response;
170
- expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.INVALID_API_KEY);
171
- expect(errorResponse.error.type).toBe('authentication_error');
172
- expect(errorResponse.error.message).toContain('Invalid API key');
173
- });
174
- it('should handle rate limit errors (429)', async () => {
175
- const apiError = new Error('Rate limit exceeded');
176
- apiError.status = 429;
177
- mockCreate.mockRejectedValueOnce(apiError);
178
- const response = await adapter.sendMessage(basicRequest, 'test-key');
179
- const errorResponse = response;
180
- expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.RATE_LIMIT_EXCEEDED);
181
- expect(errorResponse.error.type).toBe('rate_limit_error');
182
- });
183
- it('should handle insufficient quota errors (429 with specific message)', async () => {
184
- const apiError = new Error('You exceeded your current quota, please check your plan and billing details');
185
- apiError.status = 429;
186
- mockCreate.mockRejectedValueOnce(apiError);
187
- const response = await adapter.sendMessage(basicRequest, 'test-key');
188
- const errorResponse = response;
189
- // Without special handling for quota messages, this is just a rate limit error
190
- expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.RATE_LIMIT_EXCEEDED);
191
- expect(errorResponse.error.type).toBe('rate_limit_error');
192
- });
193
- it('should handle context length errors (400)', async () => {
194
- const apiError = new Error("This model's maximum context length is 4096 tokens");
195
- apiError.status = 400;
196
- // Mock it as an OpenAI APIError
197
- Object.setPrototypeOf(apiError, openai_1.default.APIError.prototype);
198
- mockCreate.mockRejectedValueOnce(apiError);
199
- const response = await adapter.sendMessage(basicRequest, 'test-key');
200
- const errorResponse = response;
201
- expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.CONTEXT_LENGTH_EXCEEDED);
202
- expect(errorResponse.error.type).toBe('invalid_request_error');
203
- });
204
- it('should handle model not found errors (404)', async () => {
205
- const apiError = new Error('The model does not exist');
206
- apiError.status = 404;
207
- mockCreate.mockRejectedValueOnce(apiError);
208
- const response = await adapter.sendMessage(basicRequest, 'test-key');
209
- const errorResponse = response;
210
- expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.MODEL_NOT_FOUND);
211
- expect(errorResponse.error.type).toBe('invalid_request_error');
212
- });
213
- it('should handle server errors (500)', async () => {
214
- const apiError = new Error('Internal server error');
215
- apiError.status = 500;
216
- mockCreate.mockRejectedValueOnce(apiError);
217
- const response = await adapter.sendMessage(basicRequest, 'test-key');
218
- const errorResponse = response;
219
- expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.PROVIDER_ERROR);
220
- expect(errorResponse.error.type).toBe('server_error');
221
- });
222
- it('should handle network errors', async () => {
223
- const networkError = new Error('Network error');
224
- networkError.code = 'ECONNREFUSED';
225
- mockCreate.mockRejectedValueOnce(networkError);
226
- const response = await adapter.sendMessage(basicRequest, 'test-key');
227
- const errorResponse = response;
228
- expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.NETWORK_ERROR);
229
- expect(errorResponse.error.type).toBe('connection_error');
230
- });
231
- it('should handle unknown errors', async () => {
232
- mockCreate.mockRejectedValueOnce(new Error('Unknown error'));
233
- const response = await adapter.sendMessage(basicRequest, 'test-key');
234
- const errorResponse = response;
235
- expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.UNKNOWN_ERROR);
236
- expect(errorResponse.error.message).toContain('Unknown error');
237
- });
238
- });
239
- });
240
- describe('validateApiKey', () => {
241
- it('should validate API key format', () => {
242
- expect(adapter.validateApiKey('sk-test123456789012345678')).toBe(true);
243
- expect(adapter.validateApiKey('sk-proj-test123456789012')).toBe(true);
244
- expect(adapter.validateApiKey('invalid')).toBe(false);
245
- expect(adapter.validateApiKey('')).toBe(false);
246
- expect(adapter.validateApiKey('sk-short')).toBe(false); // Too short
247
- });
248
- });
249
- describe('getAdapterInfo', () => {
250
- it('should return correct adapter information', () => {
251
- const info = adapter.getAdapterInfo();
252
- expect(info.providerId).toBe('openai');
253
- expect(info.name).toBe('OpenAI Client Adapter');
254
- expect(info.version).toBeDefined();
255
- // supportedModels is not part of the interface
256
- });
257
- });
258
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,123 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const adapterErrorUtils_1 = require("./adapterErrorUtils");
4
- const types_1 = require("./types");
5
- describe('adapterErrorUtils', () => {
6
- describe('getCommonMappedErrorDetails', () => {
7
- it('should map 400 status to provider error', () => {
8
- const error = { status: 400, message: 'Bad request' };
9
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error);
10
- expect(result.errorCode).toBe(types_1.ADAPTER_ERROR_CODES.PROVIDER_ERROR);
11
- expect(result.errorType).toBe('invalid_request_error');
12
- expect(result.errorMessage).toBe('Bad request');
13
- expect(result.status).toBe(400);
14
- });
15
- it('should map 401 status to invalid API key', () => {
16
- const error = { status: 401, message: 'Unauthorized' };
17
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error);
18
- expect(result.errorCode).toBe(types_1.ADAPTER_ERROR_CODES.INVALID_API_KEY);
19
- expect(result.errorType).toBe('authentication_error');
20
- expect(result.errorMessage).toBe('Unauthorized');
21
- expect(result.status).toBe(401);
22
- });
23
- it('should map 402 status to insufficient credits', () => {
24
- const error = { status: 402, message: 'Payment required' };
25
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error);
26
- expect(result.errorCode).toBe(types_1.ADAPTER_ERROR_CODES.INSUFFICIENT_CREDITS);
27
- expect(result.errorType).toBe('rate_limit_error');
28
- expect(result.errorMessage).toBe('Payment required');
29
- expect(result.status).toBe(402);
30
- });
31
- it('should map 404 status to model not found', () => {
32
- const error = { status: 404, message: 'Not found' };
33
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error);
34
- expect(result.errorCode).toBe(types_1.ADAPTER_ERROR_CODES.MODEL_NOT_FOUND);
35
- expect(result.errorType).toBe('invalid_request_error');
36
- expect(result.errorMessage).toBe('Not found');
37
- expect(result.status).toBe(404);
38
- });
39
- it('should map 429 status to rate limit exceeded', () => {
40
- const error = { status: 429, message: 'Too many requests' };
41
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error);
42
- expect(result.errorCode).toBe(types_1.ADAPTER_ERROR_CODES.RATE_LIMIT_EXCEEDED);
43
- expect(result.errorType).toBe('rate_limit_error');
44
- expect(result.errorMessage).toBe('Too many requests');
45
- expect(result.status).toBe(429);
46
- });
47
- it('should map 5xx status codes to provider error', () => {
48
- const testCases = [500, 502, 503, 504];
49
- testCases.forEach(status => {
50
- const error = { status, message: `Server error ${status}` };
51
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error);
52
- expect(result.errorCode).toBe(types_1.ADAPTER_ERROR_CODES.PROVIDER_ERROR);
53
- expect(result.errorType).toBe('server_error');
54
- expect(result.errorMessage).toBe(`Server error ${status}`);
55
- expect(result.status).toBe(status);
56
- });
57
- });
58
- it('should map other 4xx status codes to provider error', () => {
59
- const error = { status: 403, message: 'Forbidden' };
60
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error);
61
- expect(result.errorCode).toBe(types_1.ADAPTER_ERROR_CODES.PROVIDER_ERROR);
62
- expect(result.errorType).toBe('invalid_request_error');
63
- expect(result.errorMessage).toBe('Forbidden');
64
- expect(result.status).toBe(403);
65
- });
66
- it('should map network errors to network error', () => {
67
- const networkErrors = [
68
- { code: 'ENOTFOUND', message: 'DNS lookup failed' },
69
- { code: 'ECONNREFUSED', message: 'Connection refused' },
70
- { code: 'ETIMEDOUT', message: 'Request timed out' },
71
- { name: 'ConnectTimeoutError', message: 'Connection timeout' },
72
- { type: 'request-timeout', message: 'Request timeout' }
73
- ];
74
- networkErrors.forEach(error => {
75
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error);
76
- expect(result.errorCode).toBe(types_1.ADAPTER_ERROR_CODES.NETWORK_ERROR);
77
- expect(result.errorType).toBe('connection_error');
78
- expect(result.errorMessage).toBe(error.message);
79
- expect(result.status).toBeUndefined();
80
- });
81
- });
82
- it('should handle generic Error instances', () => {
83
- const error = new Error('Something went wrong');
84
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error);
85
- expect(result.errorCode).toBe(types_1.ADAPTER_ERROR_CODES.UNKNOWN_ERROR);
86
- expect(result.errorType).toBe('client_error');
87
- expect(result.errorMessage).toBe('Something went wrong');
88
- expect(result.status).toBeUndefined();
89
- });
90
- it('should handle unknown error types', () => {
91
- const testCases = [
92
- null,
93
- undefined,
94
- 'string error',
95
- 123,
96
- { random: 'object' }
97
- ];
98
- testCases.forEach(error => {
99
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error);
100
- expect(result.errorCode).toBe(types_1.ADAPTER_ERROR_CODES.UNKNOWN_ERROR);
101
- expect(result.errorType).toBe('server_error');
102
- expect(result.errorMessage).toBe('Unknown error occurred');
103
- expect(result.status).toBeUndefined();
104
- });
105
- });
106
- it('should use provider message override when provided', () => {
107
- const error = { status: 400, message: 'Original message' };
108
- const override = 'Custom error message';
109
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error, override);
110
- expect(result.errorMessage).toBe(override);
111
- });
112
- it('should handle errors without message property', () => {
113
- const error = { status: 400 };
114
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error);
115
- expect(result.errorMessage).toBe('HTTP 400 error');
116
- });
117
- it('should handle errors with empty message', () => {
118
- const error = { status: 400, message: '' };
119
- const result = (0, adapterErrorUtils_1.getCommonMappedErrorDetails)(error);
120
- expect(result.errorMessage).toBe('HTTP 400 error');
121
- });
122
- });
123
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,176 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const config_1 = require("./config");
4
- describe('LLM Config', () => {
5
- describe('isProviderSupported', () => {
6
- it('should correctly identify supported providers', () => {
7
- expect((0, config_1.isProviderSupported)('openai')).toBe(true);
8
- expect((0, config_1.isProviderSupported)('anthropic')).toBe(true);
9
- expect((0, config_1.isProviderSupported)('gemini')).toBe(true);
10
- });
11
- it('should return false for unsupported providers', () => {
12
- expect((0, config_1.isProviderSupported)('unsupported-provider')).toBe(false);
13
- expect((0, config_1.isProviderSupported)('')).toBe(false);
14
- });
15
- });
16
- describe('getProviderById', () => {
17
- it('should return provider info for valid providers', () => {
18
- const openaiProvider = (0, config_1.getProviderById)('openai');
19
- expect(openaiProvider).toBeDefined();
20
- expect(openaiProvider?.id).toBe('openai');
21
- expect(openaiProvider?.name).toBe('OpenAI');
22
- });
23
- it('should return undefined for invalid providers', () => {
24
- expect((0, config_1.getProviderById)('unsupported-provider')).toBeUndefined();
25
- expect((0, config_1.getProviderById)('')).toBeUndefined();
26
- });
27
- });
28
- describe('getModelById', () => {
29
- it('should return model info for valid model and provider combination', () => {
30
- const model = (0, config_1.getModelById)('gpt-4.1', 'openai');
31
- expect(model).toBeDefined();
32
- expect(model?.id).toBe('gpt-4.1');
33
- expect(model?.providerId).toBe('openai');
34
- });
35
- it('should return undefined for invalid model or provider', () => {
36
- expect((0, config_1.getModelById)('invalid-model', 'openai')).toBeUndefined();
37
- expect((0, config_1.getModelById)('gpt-4.1', 'anthropic')).toBeUndefined();
38
- expect((0, config_1.getModelById)('gpt-4.1', 'invalid-provider')).toBeUndefined();
39
- });
40
- });
41
- describe('getModelsByProvider', () => {
42
- it('should return models for valid providers', () => {
43
- const openaiModels = (0, config_1.getModelsByProvider)('openai');
44
- expect(openaiModels.length).toBeGreaterThan(0);
45
- expect(openaiModels.every(model => model.providerId === 'openai')).toBe(true);
46
- });
47
- it('should return empty array for invalid providers', () => {
48
- expect((0, config_1.getModelsByProvider)('invalid-provider')).toEqual([]);
49
- expect((0, config_1.getModelsByProvider)('')).toEqual([]);
50
- });
51
- });
52
- describe('isModelSupported', () => {
53
- it('should correctly identify supported model/provider combinations', () => {
54
- expect((0, config_1.isModelSupported)('gpt-4.1', 'openai')).toBe(true);
55
- expect((0, config_1.isModelSupported)('claude-sonnet-4-20250514', 'anthropic')).toBe(true);
56
- expect((0, config_1.isModelSupported)('gemini-2.5-pro', 'gemini')).toBe(true);
57
- });
58
- it('should return false for unsupported combinations', () => {
59
- expect((0, config_1.isModelSupported)('gpt-4.1', 'anthropic')).toBe(false);
60
- expect((0, config_1.isModelSupported)('claude-sonnet-4-20250514', 'openai')).toBe(false);
61
- expect((0, config_1.isModelSupported)('invalid-model', 'openai')).toBe(false);
62
- });
63
- });
64
- describe('getDefaultSettingsForModel', () => {
65
- it('should return default settings for valid models', () => {
66
- const settings = (0, config_1.getDefaultSettingsForModel)('gpt-4.1', 'openai');
67
- expect(settings).toBeDefined();
68
- expect(settings.temperature).toBeDefined();
69
- expect(settings.maxTokens).toBeDefined();
70
- expect(settings.topP).toBeDefined();
71
- });
72
- it('should apply model-specific overrides', () => {
73
- const gpt4Settings = (0, config_1.getDefaultSettingsForModel)('gpt-4.1', 'openai');
74
- const gpt4MiniSettings = (0, config_1.getDefaultSettingsForModel)('gpt-4.1-mini', 'openai');
75
- // These might have different maxTokens based on model capabilities
76
- expect(gpt4Settings.maxTokens).toBeDefined();
77
- expect(gpt4MiniSettings.maxTokens).toBeDefined();
78
- });
79
- });
80
- describe('validateLLMSettings', () => {
81
- it('should return empty array for valid settings', () => {
82
- const validSettings = {
83
- temperature: 0.7,
84
- maxTokens: 1000,
85
- topP: 0.9,
86
- frequencyPenalty: 0.5,
87
- presencePenalty: -0.5,
88
- stopSequences: ['\\n', 'END'],
89
- user: 'test-user'
90
- };
91
- expect((0, config_1.validateLLMSettings)(validSettings)).toEqual([]);
92
- });
93
- it('should validate temperature bounds', () => {
94
- expect((0, config_1.validateLLMSettings)({ temperature: -0.1 })).toContain('temperature must be a number between 0 and 2');
95
- expect((0, config_1.validateLLMSettings)({ temperature: 2.1 })).toContain('temperature must be a number between 0 and 2');
96
- expect((0, config_1.validateLLMSettings)({ temperature: 'invalid' })).toContain('temperature must be a number between 0 and 2');
97
- });
98
- it('should validate maxTokens', () => {
99
- expect((0, config_1.validateLLMSettings)({ maxTokens: 0 })).toContain('maxTokens must be an integer between 1 and 100000');
100
- expect((0, config_1.validateLLMSettings)({ maxTokens: 100001 })).toContain('maxTokens must be an integer between 1 and 100000');
101
- expect((0, config_1.validateLLMSettings)({ maxTokens: 1.5 })).toContain('maxTokens must be an integer between 1 and 100000');
102
- });
103
- it('should validate topP bounds', () => {
104
- expect((0, config_1.validateLLMSettings)({ topP: -0.1 })).toContain('topP must be a number between 0 and 1');
105
- expect((0, config_1.validateLLMSettings)({ topP: 1.1 })).toContain('topP must be a number between 0 and 1');
106
- });
107
- it('should validate frequencyPenalty bounds', () => {
108
- expect((0, config_1.validateLLMSettings)({ frequencyPenalty: -2.1 })).toContain('frequencyPenalty must be a number between -2 and 2');
109
- expect((0, config_1.validateLLMSettings)({ frequencyPenalty: 2.1 })).toContain('frequencyPenalty must be a number between -2 and 2');
110
- });
111
- it('should validate presencePenalty bounds', () => {
112
- expect((0, config_1.validateLLMSettings)({ presencePenalty: -2.1 })).toContain('presencePenalty must be a number between -2 and 2');
113
- expect((0, config_1.validateLLMSettings)({ presencePenalty: 2.1 })).toContain('presencePenalty must be a number between -2 and 2');
114
- });
115
- it('should validate stopSequences', () => {
116
- expect((0, config_1.validateLLMSettings)({ stopSequences: 'invalid' })).toContain('stopSequences must be an array');
117
- expect((0, config_1.validateLLMSettings)({ stopSequences: ['1', '2', '3', '4', '5'] })).toContain('stopSequences can contain at most 4 sequences');
118
- expect((0, config_1.validateLLMSettings)({ stopSequences: ['valid', ''] })).toContain('stopSequences must contain only non-empty strings');
119
- expect((0, config_1.validateLLMSettings)({ stopSequences: ['valid', 123] })).toContain('stopSequences must contain only non-empty strings');
120
- });
121
- it('should validate user field', () => {
122
- expect((0, config_1.validateLLMSettings)({ user: 123 })).toContain('user must be a string');
123
- });
124
- it('should validate geminiSafetySettings', () => {
125
- const invalidSettings = { geminiSafetySettings: 'invalid' };
126
- expect((0, config_1.validateLLMSettings)(invalidSettings)).toContain('geminiSafetySettings must be an array');
127
- const invalidCategory = {
128
- geminiSafetySettings: [
129
- { category: 'INVALID_CATEGORY', threshold: 'BLOCK_NONE' }
130
- ]
131
- };
132
- expect((0, config_1.validateLLMSettings)(invalidCategory)).toContain('geminiSafetySettings[0].category must be a valid Gemini harm category');
133
- const invalidThreshold = {
134
- geminiSafetySettings: [
135
- { category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'INVALID_THRESHOLD' }
136
- ]
137
- };
138
- expect((0, config_1.validateLLMSettings)(invalidThreshold)).toContain('geminiSafetySettings[0].threshold must be a valid Gemini harm block threshold');
139
- const validGeminiSettings = {
140
- geminiSafetySettings: [
141
- { category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_NONE' }
142
- ]
143
- };
144
- expect((0, config_1.validateLLMSettings)(validGeminiSettings)).toEqual([]);
145
- });
146
- it('should validate reasoning settings', () => {
147
- // Invalid reasoning object
148
- expect((0, config_1.validateLLMSettings)({ reasoning: 'invalid' })).toContain('reasoning must be an object');
149
- // Invalid enabled value
150
- expect((0, config_1.validateLLMSettings)({ reasoning: { enabled: 'yes' } })).toContain('reasoning.enabled must be a boolean');
151
- // Invalid effort value
152
- expect((0, config_1.validateLLMSettings)({ reasoning: { effort: 'maximum' } })).toContain("reasoning.effort must be 'high', 'medium', or 'low'");
153
- expect((0, config_1.validateLLMSettings)({ reasoning: { effort: 'high' } })).toEqual([]);
154
- // Invalid maxTokens value
155
- expect((0, config_1.validateLLMSettings)({ reasoning: { maxTokens: -100 } })).toContain('reasoning.maxTokens must be a non-negative integer');
156
- expect((0, config_1.validateLLMSettings)({ reasoning: { maxTokens: 1.5 } })).toContain('reasoning.maxTokens must be a non-negative integer');
157
- expect((0, config_1.validateLLMSettings)({ reasoning: { maxTokens: 5000 } })).toEqual([]);
158
- // Invalid exclude value
159
- expect((0, config_1.validateLLMSettings)({ reasoning: { exclude: 'yes' } })).toContain('reasoning.exclude must be a boolean');
160
- // Valid reasoning settings
161
- expect((0, config_1.validateLLMSettings)({ reasoning: { enabled: true, effort: 'medium', maxTokens: 10000, exclude: false } })).toEqual([]);
162
- });
163
- it('should return multiple errors for multiple invalid fields', () => {
164
- const invalidSettings = {
165
- temperature: -1,
166
- maxTokens: 0,
167
- topP: 2
168
- };
169
- const errors = (0, config_1.validateLLMSettings)(invalidSettings);
170
- expect(errors).toHaveLength(3);
171
- expect(errors).toContain('temperature must be a number between 0 and 2');
172
- expect(errors).toContain('maxTokens must be an integer between 1 and 100000');
173
- expect(errors).toContain('topP must be a number between 0 and 1');
174
- });
175
- });
176
- });
@@ -1 +0,0 @@
1
- export {};