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,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 {};
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const RequestValidator_1 = require("./RequestValidator");
|
|
4
|
-
describe('RequestValidator', () => {
|
|
5
|
-
let validator;
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
validator = new RequestValidator_1.RequestValidator();
|
|
8
|
-
});
|
|
9
|
-
describe('validateRequestStructure', () => {
|
|
10
|
-
it('should return validation error for empty messages', () => {
|
|
11
|
-
const request = {
|
|
12
|
-
providerId: 'openai',
|
|
13
|
-
modelId: 'gpt-4.1',
|
|
14
|
-
messages: []
|
|
15
|
-
};
|
|
16
|
-
const result = validator.validateRequestStructure(request);
|
|
17
|
-
expect(result).not.toBeNull();
|
|
18
|
-
expect(result?.error.code).toBe('INVALID_REQUEST');
|
|
19
|
-
expect(result?.error.message).toContain('Request must contain at least one message');
|
|
20
|
-
});
|
|
21
|
-
it('should return validation error for invalid message role', () => {
|
|
22
|
-
const request = {
|
|
23
|
-
providerId: 'openai',
|
|
24
|
-
modelId: 'gpt-4.1',
|
|
25
|
-
messages: [{ role: 'invalid', content: 'Hello' }]
|
|
26
|
-
};
|
|
27
|
-
const result = validator.validateRequestStructure(request);
|
|
28
|
-
expect(result).not.toBeNull();
|
|
29
|
-
expect(result?.error.code).toBe('INVALID_MESSAGE_ROLE');
|
|
30
|
-
expect(result?.error.message).toContain('Invalid message role');
|
|
31
|
-
});
|
|
32
|
-
it('should return validation error for empty message content', () => {
|
|
33
|
-
const request = {
|
|
34
|
-
providerId: 'openai',
|
|
35
|
-
modelId: 'gpt-4.1',
|
|
36
|
-
messages: [{ role: 'user', content: '' }]
|
|
37
|
-
};
|
|
38
|
-
const result = validator.validateRequestStructure(request);
|
|
39
|
-
expect(result).not.toBeNull();
|
|
40
|
-
expect(result?.error.code).toBe('INVALID_MESSAGE');
|
|
41
|
-
expect(result?.error.message).toContain('Message at index 0 must have both');
|
|
42
|
-
});
|
|
43
|
-
it('should pass validation for valid request', () => {
|
|
44
|
-
const request = {
|
|
45
|
-
providerId: 'openai',
|
|
46
|
-
modelId: 'gpt-4.1',
|
|
47
|
-
messages: [{ role: 'user', content: 'Hello' }]
|
|
48
|
-
};
|
|
49
|
-
const result = validator.validateRequestStructure(request);
|
|
50
|
-
expect(result).toBeNull();
|
|
51
|
-
});
|
|
52
|
-
it('should handle preset requests correctly', () => {
|
|
53
|
-
const request = {
|
|
54
|
-
presetId: 'test-preset',
|
|
55
|
-
messages: []
|
|
56
|
-
};
|
|
57
|
-
const result = validator.validateRequestStructure(request);
|
|
58
|
-
expect(result).not.toBeNull();
|
|
59
|
-
expect(result?.provider).toBe('test-preset');
|
|
60
|
-
expect(result?.model).toBe('test-preset');
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
describe('validateSettings', () => {
|
|
64
|
-
it('should return error for invalid temperature', () => {
|
|
65
|
-
const settings = { temperature: 2.5 };
|
|
66
|
-
const result = validator.validateSettings(settings, 'openai', 'gpt-4.1');
|
|
67
|
-
expect(result).not.toBeNull();
|
|
68
|
-
expect(result?.error.code).toBe('INVALID_SETTINGS');
|
|
69
|
-
expect(result?.error.message).toContain('temperature must be a number between');
|
|
70
|
-
});
|
|
71
|
-
it('should return error for invalid maxTokens', () => {
|
|
72
|
-
const settings = { maxTokens: 0 };
|
|
73
|
-
const result = validator.validateSettings(settings, 'openai', 'gpt-4.1');
|
|
74
|
-
expect(result).not.toBeNull();
|
|
75
|
-
expect(result?.error.code).toBe('INVALID_SETTINGS');
|
|
76
|
-
expect(result?.error.message).toContain('maxTokens must be an integer between');
|
|
77
|
-
});
|
|
78
|
-
it('should return error for invalid topP', () => {
|
|
79
|
-
const settings = { topP: -0.1 };
|
|
80
|
-
const result = validator.validateSettings(settings, 'openai', 'gpt-4.1');
|
|
81
|
-
expect(result).not.toBeNull();
|
|
82
|
-
expect(result?.error.code).toBe('INVALID_SETTINGS');
|
|
83
|
-
expect(result?.error.message).toContain('topP must be a number between 0 and 1');
|
|
84
|
-
});
|
|
85
|
-
it('should pass validation for valid settings', () => {
|
|
86
|
-
const settings = {
|
|
87
|
-
temperature: 0.7,
|
|
88
|
-
maxTokens: 1000,
|
|
89
|
-
topP: 0.9
|
|
90
|
-
};
|
|
91
|
-
const result = validator.validateSettings(settings, 'openai', 'gpt-4.1');
|
|
92
|
-
expect(result).toBeNull();
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
describe('validateReasoningSettings', () => {
|
|
96
|
-
const mockModelWithReasoning = {
|
|
97
|
-
id: 'gpt-4.1',
|
|
98
|
-
name: 'GPT-4.1',
|
|
99
|
-
providerId: 'openai',
|
|
100
|
-
supportsPromptCache: false,
|
|
101
|
-
reasoning: { supported: true }
|
|
102
|
-
};
|
|
103
|
-
const mockModelWithoutReasoning = {
|
|
104
|
-
id: 'gpt-4.1',
|
|
105
|
-
name: 'GPT-4.1',
|
|
106
|
-
providerId: 'openai',
|
|
107
|
-
supportsPromptCache: false,
|
|
108
|
-
reasoning: { supported: false }
|
|
109
|
-
};
|
|
110
|
-
const baseRequest = {
|
|
111
|
-
providerId: 'openai',
|
|
112
|
-
modelId: 'gpt-4.1',
|
|
113
|
-
messages: [{ role: 'user', content: 'Hello' }]
|
|
114
|
-
};
|
|
115
|
-
it('should pass validation when no reasoning settings provided', () => {
|
|
116
|
-
const result = validator.validateReasoningSettings(mockModelWithoutReasoning, undefined, baseRequest);
|
|
117
|
-
expect(result).toBeNull();
|
|
118
|
-
});
|
|
119
|
-
it('should reject reasoning settings for non-reasoning models', () => {
|
|
120
|
-
const reasoning = { enabled: true };
|
|
121
|
-
const result = validator.validateReasoningSettings(mockModelWithoutReasoning, reasoning, baseRequest);
|
|
122
|
-
expect(result).not.toBeNull();
|
|
123
|
-
expect(result?.error.code).toBe('reasoning_not_supported');
|
|
124
|
-
expect(result?.error.message).toContain('does not support reasoning/thinking');
|
|
125
|
-
});
|
|
126
|
-
it('should reject reasoning with effort for non-reasoning models', () => {
|
|
127
|
-
const reasoning = { effort: 'high' };
|
|
128
|
-
const result = validator.validateReasoningSettings(mockModelWithoutReasoning, reasoning, baseRequest);
|
|
129
|
-
expect(result).not.toBeNull();
|
|
130
|
-
expect(result?.error.code).toBe('reasoning_not_supported');
|
|
131
|
-
});
|
|
132
|
-
it('should reject reasoning with maxTokens for non-reasoning models', () => {
|
|
133
|
-
const reasoning = { maxTokens: 5000 };
|
|
134
|
-
const result = validator.validateReasoningSettings(mockModelWithoutReasoning, reasoning, baseRequest);
|
|
135
|
-
expect(result).not.toBeNull();
|
|
136
|
-
expect(result?.error.code).toBe('reasoning_not_supported');
|
|
137
|
-
});
|
|
138
|
-
it('should allow disabled reasoning for non-reasoning models', () => {
|
|
139
|
-
const reasoning = { enabled: false };
|
|
140
|
-
const result = validator.validateReasoningSettings(mockModelWithoutReasoning, reasoning, baseRequest);
|
|
141
|
-
expect(result).toBeNull();
|
|
142
|
-
});
|
|
143
|
-
it('should allow reasoning with exclude=true for non-reasoning models', () => {
|
|
144
|
-
const reasoning = { exclude: true };
|
|
145
|
-
const result = validator.validateReasoningSettings(mockModelWithoutReasoning, reasoning, baseRequest);
|
|
146
|
-
expect(result).toBeNull();
|
|
147
|
-
});
|
|
148
|
-
it('should allow all reasoning settings for models that support reasoning', () => {
|
|
149
|
-
const reasoning = {
|
|
150
|
-
enabled: true,
|
|
151
|
-
effort: 'high',
|
|
152
|
-
maxTokens: 5000,
|
|
153
|
-
exclude: false
|
|
154
|
-
};
|
|
155
|
-
const result = validator.validateReasoningSettings(mockModelWithReasoning, reasoning, baseRequest);
|
|
156
|
-
expect(result).toBeNull();
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const SettingsManager_1 = require("./SettingsManager");
|
|
4
|
-
const config_1 = require("../config");
|
|
5
|
-
jest.mock('../config', () => ({
|
|
6
|
-
getDefaultSettingsForModel: jest.fn().mockReturnValue({
|
|
7
|
-
temperature: 0.7,
|
|
8
|
-
maxTokens: 1000,
|
|
9
|
-
topP: 0.9,
|
|
10
|
-
stopSequences: [],
|
|
11
|
-
frequencyPenalty: 0,
|
|
12
|
-
presencePenalty: 0,
|
|
13
|
-
user: '',
|
|
14
|
-
supportsSystemMessage: true,
|
|
15
|
-
geminiSafetySettings: [],
|
|
16
|
-
reasoning: {
|
|
17
|
-
enabled: false,
|
|
18
|
-
effort: undefined,
|
|
19
|
-
maxTokens: undefined,
|
|
20
|
-
exclude: false,
|
|
21
|
-
},
|
|
22
|
-
thinkingExtraction: {
|
|
23
|
-
enabled: true,
|
|
24
|
-
tag: 'thinking',
|
|
25
|
-
},
|
|
26
|
-
}),
|
|
27
|
-
}));
|
|
28
|
-
describe('SettingsManager', () => {
|
|
29
|
-
let settingsManager;
|
|
30
|
-
beforeEach(() => {
|
|
31
|
-
settingsManager = new SettingsManager_1.SettingsManager();
|
|
32
|
-
jest.clearAllMocks();
|
|
33
|
-
});
|
|
34
|
-
describe('mergeSettingsForModel', () => {
|
|
35
|
-
it('should return default settings when no request settings provided', () => {
|
|
36
|
-
const result = settingsManager.mergeSettingsForModel('gpt-4.1', 'openai');
|
|
37
|
-
expect(config_1.getDefaultSettingsForModel).toHaveBeenCalledWith('gpt-4.1', 'openai');
|
|
38
|
-
expect(result).toEqual({
|
|
39
|
-
temperature: 0.7,
|
|
40
|
-
maxTokens: 1000,
|
|
41
|
-
topP: 0.9,
|
|
42
|
-
stopSequences: [],
|
|
43
|
-
frequencyPenalty: 0,
|
|
44
|
-
presencePenalty: 0,
|
|
45
|
-
user: '',
|
|
46
|
-
supportsSystemMessage: true,
|
|
47
|
-
geminiSafetySettings: [],
|
|
48
|
-
reasoning: {
|
|
49
|
-
enabled: false,
|
|
50
|
-
effort: undefined,
|
|
51
|
-
maxTokens: undefined,
|
|
52
|
-
exclude: false,
|
|
53
|
-
},
|
|
54
|
-
thinkingExtraction: {
|
|
55
|
-
enabled: true,
|
|
56
|
-
tag: 'thinking',
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
it('should merge user settings with defaults', () => {
|
|
61
|
-
const userSettings = {
|
|
62
|
-
temperature: 0.9,
|
|
63
|
-
maxTokens: 500,
|
|
64
|
-
};
|
|
65
|
-
const result = settingsManager.mergeSettingsForModel('gpt-4.1', 'openai', userSettings);
|
|
66
|
-
expect(result.temperature).toBe(0.9);
|
|
67
|
-
expect(result.maxTokens).toBe(500);
|
|
68
|
-
// Other settings should remain as defaults
|
|
69
|
-
expect(result.topP).toBe(0.9);
|
|
70
|
-
expect(result.frequencyPenalty).toBe(0);
|
|
71
|
-
});
|
|
72
|
-
it('should handle reasoning settings override', () => {
|
|
73
|
-
const userSettings = {
|
|
74
|
-
reasoning: {
|
|
75
|
-
enabled: true,
|
|
76
|
-
effort: 'high',
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
const result = settingsManager.mergeSettingsForModel('claude-3-7-sonnet-20250219', 'anthropic', userSettings);
|
|
80
|
-
expect(result.reasoning).toEqual({
|
|
81
|
-
enabled: true,
|
|
82
|
-
effort: 'high',
|
|
83
|
-
maxTokens: undefined,
|
|
84
|
-
exclude: false,
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
it('should handle complex settings including Gemini safety settings', () => {
|
|
88
|
-
const userSettings = {
|
|
89
|
-
temperature: 0.5,
|
|
90
|
-
geminiSafetySettings: [
|
|
91
|
-
{ category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_NONE' },
|
|
92
|
-
],
|
|
93
|
-
};
|
|
94
|
-
const result = settingsManager.mergeSettingsForModel('gemini-2.0-flash', 'gemini', userSettings);
|
|
95
|
-
expect(result.temperature).toBe(0.5);
|
|
96
|
-
expect(result.geminiSafetySettings).toHaveLength(1);
|
|
97
|
-
expect(result.geminiSafetySettings[0]).toEqual({
|
|
98
|
-
category: 'HARM_CATEGORY_HATE_SPEECH',
|
|
99
|
-
threshold: 'BLOCK_NONE',
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
it('should handle all optional fields being provided', () => {
|
|
103
|
-
const userSettings = {
|
|
104
|
-
temperature: 0.8,
|
|
105
|
-
maxTokens: 2000,
|
|
106
|
-
topP: 0.95,
|
|
107
|
-
stopSequences: ['END', 'STOP'],
|
|
108
|
-
frequencyPenalty: 0.5,
|
|
109
|
-
presencePenalty: -0.5,
|
|
110
|
-
user: 'test-user',
|
|
111
|
-
supportsSystemMessage: false,
|
|
112
|
-
geminiSafetySettings: [],
|
|
113
|
-
reasoning: {
|
|
114
|
-
enabled: true,
|
|
115
|
-
effort: 'medium',
|
|
116
|
-
maxTokens: 5000,
|
|
117
|
-
exclude: true,
|
|
118
|
-
},
|
|
119
|
-
thinkingExtraction: {
|
|
120
|
-
enabled: false,
|
|
121
|
-
tag: 'scratchpad',
|
|
122
|
-
},
|
|
123
|
-
};
|
|
124
|
-
const result = settingsManager.mergeSettingsForModel('gpt-4.1', 'openai', userSettings);
|
|
125
|
-
expect(result).toEqual(userSettings);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
describe('filterUnsupportedParameters', () => {
|
|
129
|
-
const baseSettings = {
|
|
130
|
-
temperature: 0.7,
|
|
131
|
-
maxTokens: 1000,
|
|
132
|
-
topP: 0.9,
|
|
133
|
-
stopSequences: [],
|
|
134
|
-
frequencyPenalty: 0,
|
|
135
|
-
presencePenalty: 0,
|
|
136
|
-
user: '',
|
|
137
|
-
supportsSystemMessage: true,
|
|
138
|
-
geminiSafetySettings: [],
|
|
139
|
-
reasoning: {
|
|
140
|
-
enabled: false,
|
|
141
|
-
effort: undefined,
|
|
142
|
-
maxTokens: undefined,
|
|
143
|
-
exclude: false,
|
|
144
|
-
},
|
|
145
|
-
thinkingExtraction: {
|
|
146
|
-
enabled: true,
|
|
147
|
-
tag: 'thinking',
|
|
148
|
-
},
|
|
149
|
-
};
|
|
150
|
-
const mockModelInfo = {
|
|
151
|
-
id: 'test-model',
|
|
152
|
-
providerId: 'test-provider',
|
|
153
|
-
name: 'Test Model',
|
|
154
|
-
supportsPromptCache: false,
|
|
155
|
-
contextWindow: 4096,
|
|
156
|
-
maxTokens: 1000,
|
|
157
|
-
reasoning: { supported: true }, // Add reasoning support to prevent it being stripped
|
|
158
|
-
};
|
|
159
|
-
const mockProviderInfo = {
|
|
160
|
-
id: 'test-provider',
|
|
161
|
-
name: 'Test Provider',
|
|
162
|
-
};
|
|
163
|
-
it('should return settings unchanged when no parameters need filtering', () => {
|
|
164
|
-
const result = settingsManager.filterUnsupportedParameters(baseSettings, mockModelInfo, mockProviderInfo);
|
|
165
|
-
expect(result).toEqual(baseSettings);
|
|
166
|
-
});
|
|
167
|
-
it('should filter out provider-level unsupported parameters', () => {
|
|
168
|
-
const providerWithExclusions = {
|
|
169
|
-
...mockProviderInfo,
|
|
170
|
-
unsupportedParameters: ['frequencyPenalty', 'presencePenalty'],
|
|
171
|
-
};
|
|
172
|
-
const settingsWithPenalties = {
|
|
173
|
-
...baseSettings,
|
|
174
|
-
frequencyPenalty: 0.5,
|
|
175
|
-
presencePenalty: -0.5,
|
|
176
|
-
};
|
|
177
|
-
const result = settingsManager.filterUnsupportedParameters(settingsWithPenalties, mockModelInfo, providerWithExclusions);
|
|
178
|
-
expect(result.frequencyPenalty).toBeUndefined();
|
|
179
|
-
expect(result.presencePenalty).toBeUndefined();
|
|
180
|
-
expect(result.temperature).toBe(0.7); // Should remain unchanged
|
|
181
|
-
});
|
|
182
|
-
it('should filter out model-level unsupported parameters', () => {
|
|
183
|
-
const modelWithExclusions = {
|
|
184
|
-
...mockModelInfo,
|
|
185
|
-
unsupportedParameters: ['stopSequences', 'user'],
|
|
186
|
-
};
|
|
187
|
-
const settingsWithExcluded = {
|
|
188
|
-
...baseSettings,
|
|
189
|
-
stopSequences: ['END'],
|
|
190
|
-
user: 'test-user',
|
|
191
|
-
};
|
|
192
|
-
const result = settingsManager.filterUnsupportedParameters(settingsWithExcluded, modelWithExclusions, mockProviderInfo);
|
|
193
|
-
expect(result.stopSequences).toBeUndefined();
|
|
194
|
-
expect(result.user).toBeUndefined();
|
|
195
|
-
});
|
|
196
|
-
it('should combine provider and model exclusions', () => {
|
|
197
|
-
const providerWithExclusions = {
|
|
198
|
-
...mockProviderInfo,
|
|
199
|
-
unsupportedParameters: ['frequencyPenalty'],
|
|
200
|
-
};
|
|
201
|
-
const modelWithExclusions = {
|
|
202
|
-
...mockModelInfo,
|
|
203
|
-
unsupportedParameters: ['presencePenalty', 'stopSequences'],
|
|
204
|
-
};
|
|
205
|
-
const settingsWithAll = {
|
|
206
|
-
...baseSettings,
|
|
207
|
-
frequencyPenalty: 0.5,
|
|
208
|
-
presencePenalty: -0.5,
|
|
209
|
-
stopSequences: ['END'],
|
|
210
|
-
};
|
|
211
|
-
const result = settingsManager.filterUnsupportedParameters(settingsWithAll, modelWithExclusions, providerWithExclusions);
|
|
212
|
-
expect(result.frequencyPenalty).toBeUndefined();
|
|
213
|
-
expect(result.presencePenalty).toBeUndefined();
|
|
214
|
-
expect(result.stopSequences).toBeUndefined();
|
|
215
|
-
});
|
|
216
|
-
it('should remove reasoning settings for non-reasoning models', () => {
|
|
217
|
-
const nonReasoningModel = {
|
|
218
|
-
...mockModelInfo,
|
|
219
|
-
reasoning: { supported: false },
|
|
220
|
-
};
|
|
221
|
-
const settingsWithReasoning = {
|
|
222
|
-
...baseSettings,
|
|
223
|
-
reasoning: {
|
|
224
|
-
enabled: true,
|
|
225
|
-
effort: 'high',
|
|
226
|
-
maxTokens: 5000,
|
|
227
|
-
exclude: false,
|
|
228
|
-
},
|
|
229
|
-
};
|
|
230
|
-
const result = settingsManager.filterUnsupportedParameters(settingsWithReasoning, nonReasoningModel, mockProviderInfo);
|
|
231
|
-
expect(result.reasoning).toBeUndefined();
|
|
232
|
-
});
|
|
233
|
-
it('should keep reasoning settings for reasoning-supported models', () => {
|
|
234
|
-
const reasoningModel = {
|
|
235
|
-
...mockModelInfo,
|
|
236
|
-
reasoning: { supported: true },
|
|
237
|
-
};
|
|
238
|
-
const settingsWithReasoning = {
|
|
239
|
-
...baseSettings,
|
|
240
|
-
reasoning: {
|
|
241
|
-
enabled: true,
|
|
242
|
-
effort: 'high',
|
|
243
|
-
maxTokens: 5000,
|
|
244
|
-
exclude: false,
|
|
245
|
-
},
|
|
246
|
-
};
|
|
247
|
-
const result = settingsManager.filterUnsupportedParameters(settingsWithReasoning, reasoningModel, mockProviderInfo);
|
|
248
|
-
expect(result.reasoning).toEqual(settingsWithReasoning.reasoning);
|
|
249
|
-
});
|
|
250
|
-
it('should handle geminiSafetySettings appropriately', () => {
|
|
251
|
-
const geminiProvider = {
|
|
252
|
-
id: 'gemini',
|
|
253
|
-
name: 'Google Gemini',
|
|
254
|
-
};
|
|
255
|
-
const settingsWithGemini = {
|
|
256
|
-
...baseSettings,
|
|
257
|
-
geminiSafetySettings: [
|
|
258
|
-
{ category: 'HARM_CATEGORY_HATE_SPEECH', threshold: 'BLOCK_NONE' },
|
|
259
|
-
],
|
|
260
|
-
};
|
|
261
|
-
const result = settingsManager.filterUnsupportedParameters(settingsWithGemini, mockModelInfo, geminiProvider);
|
|
262
|
-
// Should not be filtered out for Gemini provider
|
|
263
|
-
expect(result.geminiSafetySettings).toEqual(settingsWithGemini.geminiSafetySettings);
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Prompt builder utilities for constructing structured LLM messages
|
|
3
|
-
*
|
|
4
|
-
* This module provides functions to construct the final, structured prompts
|
|
5
|
-
* that will be sent to the LLM service. This is the assembly step that turns
|
|
6
|
-
* templates and content into the format required by the LLMService.
|
|
7
|
-
*/
|
|
8
|
-
import type { LLMMessage } from '../llm/types';
|
|
9
|
-
/**
|
|
10
|
-
* Builds an array of LLM messages from a template string with role tags.
|
|
11
|
-
*
|
|
12
|
-
* This function takes a template with <SYSTEM>, <USER>, and <ASSISTANT> tags
|
|
13
|
-
* and constructs a properly formatted array of LLMMessage objects ready to be
|
|
14
|
-
* sent to an LLM service.
|
|
15
|
-
*
|
|
16
|
-
* @deprecated Use `LLMService.createMessages` for a more integrated experience that includes
|
|
17
|
-
* model-aware template rendering. For standalone, model-agnostic role tag parsing, consider
|
|
18
|
-
* using the new `parseRoleTags` utility directly.
|
|
19
|
-
*
|
|
20
|
-
* @param template The template string with {{variables}} and <ROLE> tags.
|
|
21
|
-
* @param variables An object with values to substitute into the template.
|
|
22
|
-
* @returns An array of LLMMessage objects.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* const template = `
|
|
26
|
-
* <SYSTEM>You are a helpful assistant specialized in {{expertise}}.</SYSTEM>
|
|
27
|
-
* <USER>Help me with {{task}}</USER>
|
|
28
|
-
* <ASSISTANT>I'll help you with {{task}}. Let me explain...</ASSISTANT>
|
|
29
|
-
* <USER>Can you provide more details?</USER>
|
|
30
|
-
* `;
|
|
31
|
-
*
|
|
32
|
-
* const messages = buildMessagesFromTemplate(template, {
|
|
33
|
-
* expertise: 'TypeScript',
|
|
34
|
-
* task: 'understanding generics'
|
|
35
|
-
* });
|
|
36
|
-
* // Returns an array of LLMMessage objects with roles and content
|
|
37
|
-
*/
|
|
38
|
-
export declare function buildMessagesFromTemplate(template: string, variables?: Record<string, any>): LLMMessage[];
|