genai-lite 0.3.3 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +421 -51
- package/dist/index.d.ts +5 -0
- package/dist/index.js +8 -1
- package/dist/llm/LLMService.d.ts +29 -2
- package/dist/llm/LLMService.js +67 -36
- package/dist/llm/clients/LlamaCppClientAdapter.d.ts +116 -0
- package/dist/llm/clients/LlamaCppClientAdapter.js +289 -0
- package/dist/llm/clients/LlamaCppServerClient.d.ts +161 -0
- package/dist/llm/clients/LlamaCppServerClient.js +192 -0
- package/dist/llm/config.d.ts +12 -0
- package/dist/llm/config.js +81 -4
- package/dist/llm/services/ModelResolver.js +13 -13
- package/dist/llm/services/SettingsManager.js +17 -11
- package/dist/llm/types.d.ts +87 -22
- package/dist/prompting/parser.d.ts +2 -2
- package/dist/prompting/parser.js +2 -2
- package/dist/providers/fromEnvironment.d.ts +4 -0
- package/dist/providers/fromEnvironment.js +8 -0
- package/package.json +1 -1
- package/dist/llm/LLMService.createMessages.test.d.ts +0 -4
- package/dist/llm/LLMService.createMessages.test.js +0 -364
- package/dist/llm/LLMService.original.d.ts +0 -147
- package/dist/llm/LLMService.original.js +0 -656
- package/dist/llm/LLMService.prepareMessage.test.d.ts +0 -1
- package/dist/llm/LLMService.prepareMessage.test.js +0 -303
- package/dist/llm/LLMService.presets.test.d.ts +0 -1
- package/dist/llm/LLMService.presets.test.js +0 -210
- package/dist/llm/LLMService.sendMessage.preset.test.d.ts +0 -1
- package/dist/llm/LLMService.sendMessage.preset.test.js +0 -153
- package/dist/llm/LLMService.test.d.ts +0 -1
- package/dist/llm/LLMService.test.js +0 -620
- package/dist/llm/clients/AnthropicClientAdapter.test.d.ts +0 -1
- package/dist/llm/clients/AnthropicClientAdapter.test.js +0 -273
- package/dist/llm/clients/GeminiClientAdapter.test.d.ts +0 -1
- package/dist/llm/clients/GeminiClientAdapter.test.js +0 -405
- package/dist/llm/clients/MockClientAdapter.test.d.ts +0 -1
- package/dist/llm/clients/MockClientAdapter.test.js +0 -250
- package/dist/llm/clients/OpenAIClientAdapter.test.d.ts +0 -1
- package/dist/llm/clients/OpenAIClientAdapter.test.js +0 -258
- package/dist/llm/clients/adapterErrorUtils.test.d.ts +0 -1
- package/dist/llm/clients/adapterErrorUtils.test.js +0 -123
- package/dist/llm/config.test.d.ts +0 -1
- package/dist/llm/config.test.js +0 -176
- package/dist/llm/services/AdapterRegistry.test.d.ts +0 -1
- package/dist/llm/services/AdapterRegistry.test.js +0 -239
- package/dist/llm/services/ModelResolver.test.d.ts +0 -1
- package/dist/llm/services/ModelResolver.test.js +0 -158
- package/dist/llm/services/PresetManager.test.d.ts +0 -1
- package/dist/llm/services/PresetManager.test.js +0 -210
- package/dist/llm/services/RequestValidator.test.d.ts +0 -1
- package/dist/llm/services/RequestValidator.test.js +0 -159
- package/dist/llm/services/SettingsManager.test.d.ts +0 -1
- package/dist/llm/services/SettingsManager.test.js +0 -266
- package/dist/prompting/builder.d.ts +0 -38
- package/dist/prompting/builder.js +0 -63
- package/dist/prompting/builder.test.d.ts +0 -4
- package/dist/prompting/builder.test.js +0 -109
- package/dist/prompting/content.test.d.ts +0 -4
- package/dist/prompting/content.test.js +0 -212
- package/dist/prompting/parser.test.d.ts +0 -4
- package/dist/prompting/parser.test.js +0 -464
- package/dist/prompting/template.test.d.ts +0 -1
- package/dist/prompting/template.test.js +0 -250
- package/dist/providers/fromEnvironment.test.d.ts +0 -1
- package/dist/providers/fromEnvironment.test.js +0 -46
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const MockClientAdapter_1 = require("./MockClientAdapter");
|
|
4
|
-
const types_1 = require("./types");
|
|
5
|
-
describe('MockClientAdapter', () => {
|
|
6
|
-
let adapter;
|
|
7
|
-
let basicRequest;
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
adapter = new MockClientAdapter_1.MockClientAdapter('openai');
|
|
10
|
-
basicRequest = {
|
|
11
|
-
providerId: 'openai',
|
|
12
|
-
modelId: 'mock-model',
|
|
13
|
-
messages: [{ role: 'user', content: 'Hello' }],
|
|
14
|
-
settings: {
|
|
15
|
-
temperature: 0.7,
|
|
16
|
-
maxTokens: 100,
|
|
17
|
-
topP: 1,
|
|
18
|
-
frequencyPenalty: 0,
|
|
19
|
-
presencePenalty: 0,
|
|
20
|
-
stopSequences: [],
|
|
21
|
-
user: 'test-user',
|
|
22
|
-
geminiSafetySettings: [],
|
|
23
|
-
supportsSystemMessage: true,
|
|
24
|
-
reasoning: {
|
|
25
|
-
enabled: false,
|
|
26
|
-
effort: undefined,
|
|
27
|
-
maxTokens: undefined,
|
|
28
|
-
exclude: false
|
|
29
|
-
},
|
|
30
|
-
thinkingExtraction: {
|
|
31
|
-
enabled: true,
|
|
32
|
-
tag: 'thinking'
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
});
|
|
37
|
-
describe('sendMessage', () => {
|
|
38
|
-
it('should return a successful response for basic messages', async () => {
|
|
39
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
40
|
-
expect(response.object).toBe('chat.completion');
|
|
41
|
-
expect('error' in response).toBe(false);
|
|
42
|
-
const successResponse = response;
|
|
43
|
-
expect(successResponse.provider).toBe('openai');
|
|
44
|
-
expect(successResponse.model).toBe('mock-model');
|
|
45
|
-
expect(successResponse.choices).toHaveLength(1);
|
|
46
|
-
expect(successResponse.choices[0].message.role).toBe('assistant');
|
|
47
|
-
expect(successResponse.choices[0].message.content).toContain('Hello');
|
|
48
|
-
expect(successResponse.usage).toBeDefined();
|
|
49
|
-
expect(successResponse.usage?.total_tokens).toBeGreaterThan(0);
|
|
50
|
-
});
|
|
51
|
-
describe('error simulations', () => {
|
|
52
|
-
it('should simulate invalid API key error', async () => {
|
|
53
|
-
basicRequest.messages[0].content = 'error_invalid_key';
|
|
54
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
55
|
-
expect(response.object).toBe('error');
|
|
56
|
-
const errorResponse = response;
|
|
57
|
-
expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.INVALID_API_KEY);
|
|
58
|
-
expect(errorResponse.error.type).toBe('authentication_error');
|
|
59
|
-
expect(errorResponse.error.status).toBe(401);
|
|
60
|
-
});
|
|
61
|
-
it('should simulate rate limit error', async () => {
|
|
62
|
-
basicRequest.messages[0].content = 'error_rate_limit';
|
|
63
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
64
|
-
const errorResponse = response;
|
|
65
|
-
expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.RATE_LIMIT_EXCEEDED);
|
|
66
|
-
expect(errorResponse.error.type).toBe('rate_limit_error');
|
|
67
|
-
expect(errorResponse.error.status).toBe(429);
|
|
68
|
-
});
|
|
69
|
-
it('should simulate insufficient credits error', async () => {
|
|
70
|
-
basicRequest.messages[0].content = 'error_credits';
|
|
71
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
72
|
-
const errorResponse = response;
|
|
73
|
-
expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.INSUFFICIENT_CREDITS);
|
|
74
|
-
expect(errorResponse.error.type).toBe('rate_limit_error');
|
|
75
|
-
expect(errorResponse.error.status).toBe(402);
|
|
76
|
-
});
|
|
77
|
-
it('should simulate context length exceeded error', async () => {
|
|
78
|
-
basicRequest.messages[0].content = 'error_context_length';
|
|
79
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
80
|
-
const errorResponse = response;
|
|
81
|
-
expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.CONTEXT_LENGTH_EXCEEDED);
|
|
82
|
-
expect(errorResponse.error.type).toBe('invalid_request_error');
|
|
83
|
-
expect(errorResponse.error.status).toBe(400);
|
|
84
|
-
});
|
|
85
|
-
it('should simulate model not found error', async () => {
|
|
86
|
-
basicRequest.messages[0].content = 'error_model_not_found';
|
|
87
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
88
|
-
const errorResponse = response;
|
|
89
|
-
expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.MODEL_NOT_FOUND);
|
|
90
|
-
expect(errorResponse.error.type).toBe('invalid_request_error');
|
|
91
|
-
expect(errorResponse.error.status).toBe(404);
|
|
92
|
-
});
|
|
93
|
-
it('should simulate content filter error', async () => {
|
|
94
|
-
basicRequest.messages[0].content = 'error_content_filter';
|
|
95
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
96
|
-
const errorResponse = response;
|
|
97
|
-
expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.CONTENT_FILTER);
|
|
98
|
-
expect(errorResponse.error.type).toBe('content_filter_error');
|
|
99
|
-
expect(errorResponse.error.status).toBe(400);
|
|
100
|
-
});
|
|
101
|
-
it('should simulate network error', async () => {
|
|
102
|
-
basicRequest.messages[0].content = 'error_network';
|
|
103
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
104
|
-
const errorResponse = response;
|
|
105
|
-
expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.NETWORK_ERROR);
|
|
106
|
-
expect(errorResponse.error.type).toBe('connection_error');
|
|
107
|
-
// Status is not included when it's 0
|
|
108
|
-
expect(errorResponse.error.status).toBeUndefined();
|
|
109
|
-
});
|
|
110
|
-
it('should simulate generic provider error', async () => {
|
|
111
|
-
basicRequest.messages[0].content = 'error_generic';
|
|
112
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
113
|
-
const errorResponse = response;
|
|
114
|
-
expect(errorResponse.error.code).toBe(types_1.ADAPTER_ERROR_CODES.PROVIDER_ERROR);
|
|
115
|
-
expect(errorResponse.error.type).toBe('server_error');
|
|
116
|
-
expect(errorResponse.error.status).toBe(500);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
describe('temperature effects', () => {
|
|
120
|
-
it('should generate low temperature response', async () => {
|
|
121
|
-
basicRequest.messages[0].content = 'test_temperature';
|
|
122
|
-
basicRequest.settings.temperature = 0.2;
|
|
123
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
124
|
-
const successResponse = response;
|
|
125
|
-
expect(successResponse.choices[0].message.content).toContain('Low temperature');
|
|
126
|
-
expect(successResponse.choices[0].message.content).toContain('deterministic');
|
|
127
|
-
});
|
|
128
|
-
it('should generate high temperature response', async () => {
|
|
129
|
-
basicRequest.messages[0].content = 'test_temperature';
|
|
130
|
-
basicRequest.settings.temperature = 0.9;
|
|
131
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
132
|
-
const successResponse = response;
|
|
133
|
-
expect(successResponse.choices[0].message.content).toContain('High temperature');
|
|
134
|
-
expect(successResponse.choices[0].message.content).toContain('creative');
|
|
135
|
-
});
|
|
136
|
-
it('should generate moderate temperature response', async () => {
|
|
137
|
-
basicRequest.messages[0].content = 'test_temperature';
|
|
138
|
-
basicRequest.settings.temperature = 0.5;
|
|
139
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
140
|
-
const successResponse = response;
|
|
141
|
-
expect(successResponse.choices[0].message.content).toContain('Moderate temperature');
|
|
142
|
-
expect(successResponse.choices[0].message.content).toContain('balances');
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
describe('settings effects', () => {
|
|
146
|
-
it('should respect maxTokens limit', async () => {
|
|
147
|
-
basicRequest.messages[0].content = 'long detailed response please';
|
|
148
|
-
basicRequest.settings.maxTokens = 10; // Very low limit
|
|
149
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
150
|
-
const successResponse = response;
|
|
151
|
-
const content = successResponse.choices[0].message.content;
|
|
152
|
-
const wordCount = content.split(' ').length;
|
|
153
|
-
expect(wordCount).toBeLessThanOrEqual(10); // Should be truncated
|
|
154
|
-
expect(content).toContain('...');
|
|
155
|
-
});
|
|
156
|
-
it('should respect stop sequences', async () => {
|
|
157
|
-
basicRequest.messages[0].content = 'Hello world! This is a test. STOP More content here.';
|
|
158
|
-
basicRequest.settings.stopSequences = ['STOP'];
|
|
159
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
160
|
-
const successResponse = response;
|
|
161
|
-
const content = successResponse.choices[0].message.content;
|
|
162
|
-
expect(content).not.toContain('More content here');
|
|
163
|
-
});
|
|
164
|
-
it('should generate settings test response', async () => {
|
|
165
|
-
basicRequest.messages[0].content = 'test_settings';
|
|
166
|
-
basicRequest.settings.stopSequences = []; // Empty stop sequences to avoid truncation
|
|
167
|
-
basicRequest.settings.frequencyPenalty = 0.5;
|
|
168
|
-
basicRequest.settings.presencePenalty = -0.5;
|
|
169
|
-
basicRequest.settings.maxTokens = 500;
|
|
170
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
171
|
-
const successResponse = response;
|
|
172
|
-
const content = successResponse.choices[0].message.content;
|
|
173
|
-
expect(content).toContain('Temperature: 0.7');
|
|
174
|
-
expect(content).toContain('Max Tokens: 500');
|
|
175
|
-
expect(content).toContain('Stop Sequences: none'); // When empty, it shows "none"
|
|
176
|
-
expect(content).toContain('Frequency Penalty: 0.5');
|
|
177
|
-
expect(content).toContain('Presence Penalty: -0.5');
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
describe('content-based responses', () => {
|
|
181
|
-
it('should generate greeting response', async () => {
|
|
182
|
-
basicRequest.messages[0].content = 'hello';
|
|
183
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
184
|
-
const successResponse = response;
|
|
185
|
-
expect(successResponse.choices[0].message.content).toContain('Hello!');
|
|
186
|
-
expect(successResponse.choices[0].message.content).toContain('mock LLM assistant');
|
|
187
|
-
});
|
|
188
|
-
it('should generate weather response', async () => {
|
|
189
|
-
basicRequest.messages[0].content = 'What is the weather today?';
|
|
190
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
191
|
-
const successResponse = response;
|
|
192
|
-
expect(successResponse.choices[0].message.content).toContain('weather');
|
|
193
|
-
expect(successResponse.choices[0].message.content).toContain('72°F');
|
|
194
|
-
});
|
|
195
|
-
it('should generate code response', async () => {
|
|
196
|
-
basicRequest.messages[0].content = 'Show me some code';
|
|
197
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
198
|
-
const successResponse = response;
|
|
199
|
-
expect(successResponse.choices[0].message.content).toContain('```javascript');
|
|
200
|
-
expect(successResponse.choices[0].message.content).toContain('mockFunction');
|
|
201
|
-
});
|
|
202
|
-
it('should generate long response', async () => {
|
|
203
|
-
basicRequest.messages[0].content = 'Give me a long detailed explanation';
|
|
204
|
-
basicRequest.settings.maxTokens = 1000; // Allow long response
|
|
205
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
206
|
-
const successResponse = response;
|
|
207
|
-
const content = successResponse.choices[0].message.content;
|
|
208
|
-
expect(content.length).toBeGreaterThan(500);
|
|
209
|
-
expect(content).toContain('Error Simulation');
|
|
210
|
-
expect(content).toContain('Variable Length');
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
it('should generate unique IDs for each response', async () => {
|
|
214
|
-
const response1 = await adapter.sendMessage(basicRequest, 'test-key');
|
|
215
|
-
const response2 = await adapter.sendMessage(basicRequest, 'test-key');
|
|
216
|
-
expect(response1.id).not.toBe(response2.id);
|
|
217
|
-
});
|
|
218
|
-
it('should calculate token usage', async () => {
|
|
219
|
-
basicRequest.messages[0].content = 'Calculate tokens for this message';
|
|
220
|
-
const response = await adapter.sendMessage(basicRequest, 'test-key');
|
|
221
|
-
const successResponse = response;
|
|
222
|
-
expect(successResponse.usage?.prompt_tokens).toBeGreaterThan(0);
|
|
223
|
-
expect(successResponse.usage?.completion_tokens).toBeGreaterThan(0);
|
|
224
|
-
expect(successResponse.usage?.total_tokens).toBe((successResponse.usage?.prompt_tokens ?? 0) + (successResponse.usage?.completion_tokens ?? 0));
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
describe('validateApiKey', () => {
|
|
228
|
-
it('should return true for non-empty API keys', () => {
|
|
229
|
-
expect(adapter.validateApiKey('valid-key')).toBe(true);
|
|
230
|
-
expect(adapter.validateApiKey('a')).toBe(true);
|
|
231
|
-
});
|
|
232
|
-
it('should return false for empty API keys', () => {
|
|
233
|
-
expect(adapter.validateApiKey('')).toBe(false);
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
describe('getAdapterInfo', () => {
|
|
237
|
-
it('should return correct adapter information', () => {
|
|
238
|
-
const info = adapter.getAdapterInfo();
|
|
239
|
-
expect(info.providerId).toBe('openai');
|
|
240
|
-
expect(info.name).toBe('Mock Client Adapter');
|
|
241
|
-
expect(info.version).toBe('1.0.0');
|
|
242
|
-
expect(info.supportedModels).toEqual(['mock-model-1', 'mock-model-2']);
|
|
243
|
-
});
|
|
244
|
-
it('should use custom provider ID', () => {
|
|
245
|
-
const customAdapter = new MockClientAdapter_1.MockClientAdapter('anthropic');
|
|
246
|
-
const info = customAdapter.getAdapterInfo();
|
|
247
|
-
expect(info.providerId).toBe('anthropic');
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -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 {};
|