genai-lite 0.2.0 → 0.3.0
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 +508 -30
- package/dist/config/presets.json +121 -17
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4 -3
- package/dist/llm/LLMService.createMessages.test.d.ts +4 -0
- package/dist/llm/LLMService.createMessages.test.js +364 -0
- package/dist/llm/LLMService.d.ts +49 -47
- package/dist/llm/LLMService.js +208 -303
- package/dist/llm/LLMService.original.d.ts +147 -0
- package/dist/llm/LLMService.original.js +656 -0
- package/dist/llm/LLMService.prepareMessage.test.d.ts +1 -0
- package/dist/llm/LLMService.prepareMessage.test.js +303 -0
- package/dist/llm/LLMService.sendMessage.preset.test.d.ts +1 -0
- package/dist/llm/LLMService.sendMessage.preset.test.js +153 -0
- package/dist/llm/LLMService.test.js +275 -0
- package/dist/llm/clients/AnthropicClientAdapter.js +64 -10
- package/dist/llm/clients/AnthropicClientAdapter.test.js +11 -1
- package/dist/llm/clients/GeminiClientAdapter.js +70 -11
- package/dist/llm/clients/GeminiClientAdapter.test.js +125 -1
- package/dist/llm/clients/MockClientAdapter.js +9 -3
- package/dist/llm/clients/MockClientAdapter.test.js +11 -1
- package/dist/llm/clients/OpenAIClientAdapter.js +26 -10
- package/dist/llm/clients/OpenAIClientAdapter.test.js +11 -1
- package/dist/llm/config.js +117 -2
- package/dist/llm/config.test.js +17 -0
- package/dist/llm/services/AdapterRegistry.d.ts +59 -0
- package/dist/llm/services/AdapterRegistry.js +113 -0
- package/dist/llm/services/AdapterRegistry.test.d.ts +1 -0
- package/dist/llm/services/AdapterRegistry.test.js +239 -0
- package/dist/llm/services/ModelResolver.d.ts +35 -0
- package/dist/llm/services/ModelResolver.js +116 -0
- package/dist/llm/services/ModelResolver.test.d.ts +1 -0
- package/dist/llm/services/ModelResolver.test.js +158 -0
- package/dist/llm/services/PresetManager.d.ts +27 -0
- package/dist/llm/services/PresetManager.js +50 -0
- package/dist/llm/services/PresetManager.test.d.ts +1 -0
- package/dist/llm/services/PresetManager.test.js +210 -0
- package/dist/llm/services/RequestValidator.d.ts +31 -0
- package/dist/llm/services/RequestValidator.js +122 -0
- package/dist/llm/services/RequestValidator.test.d.ts +1 -0
- package/dist/llm/services/RequestValidator.test.js +159 -0
- package/dist/llm/services/SettingsManager.d.ts +32 -0
- package/dist/llm/services/SettingsManager.js +223 -0
- package/dist/llm/services/SettingsManager.test.d.ts +1 -0
- package/dist/llm/services/SettingsManager.test.js +266 -0
- package/dist/llm/types.d.ts +107 -0
- package/dist/prompting/builder.d.ts +4 -0
- package/dist/prompting/builder.js +12 -61
- package/dist/prompting/content.js +3 -9
- package/dist/prompting/index.d.ts +2 -3
- package/dist/prompting/index.js +4 -5
- package/dist/prompting/parser.d.ts +80 -0
- package/dist/prompting/parser.js +133 -0
- package/dist/prompting/parser.test.js +348 -0
- package/dist/prompting/template.d.ts +8 -0
- package/dist/prompting/template.js +89 -6
- package/dist/prompting/template.test.js +116 -0
- package/package.json +3 -2
- package/src/config/presets.json +122 -17
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ import { LLMService, fromEnvironment } from 'genai-lite';
|
|
|
27
27
|
// Create service with environment variable API key provider
|
|
28
28
|
const llmService = new LLMService(fromEnvironment);
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// Option 1: Direct message sending
|
|
31
31
|
const response = await llmService.sendMessage({
|
|
32
32
|
providerId: 'openai',
|
|
33
33
|
modelId: 'gpt-4.1-mini',
|
|
@@ -37,6 +37,19 @@ const response = await llmService.sendMessage({
|
|
|
37
37
|
]
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
// Option 2: Create messages from template (recommended for complex prompts)
|
|
41
|
+
const { messages } = await llmService.createMessages({
|
|
42
|
+
template: '<SYSTEM>You are a helpful assistant.</SYSTEM><USER>Hello, how are you?</USER>',
|
|
43
|
+
providerId: 'openai',
|
|
44
|
+
modelId: 'gpt-4.1-mini'
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const response2 = await llmService.sendMessage({
|
|
48
|
+
providerId: 'openai',
|
|
49
|
+
modelId: 'gpt-4.1-mini',
|
|
50
|
+
messages
|
|
51
|
+
});
|
|
52
|
+
|
|
40
53
|
if (response.object === 'chat.completion') {
|
|
41
54
|
console.log(response.choices[0].message.content);
|
|
42
55
|
} else {
|
|
@@ -111,6 +124,16 @@ const llmService = new LLMService(myKeyProvider);
|
|
|
111
124
|
- `codestral-2501` - Specialized for code generation
|
|
112
125
|
- `devstral-small-2505` - Compact development-focused model
|
|
113
126
|
|
|
127
|
+
### Models with Reasoning Support
|
|
128
|
+
|
|
129
|
+
Some models include advanced reasoning/thinking capabilities that enhance their problem-solving abilities:
|
|
130
|
+
|
|
131
|
+
- **Anthropic**: Claude Sonnet 4, Claude Opus 4, Claude 3.7 Sonnet
|
|
132
|
+
- **Google Gemini**: Gemini 2.5 Pro (always on), Gemini 2.5 Flash, Gemini 2.5 Flash-Lite Preview
|
|
133
|
+
- **OpenAI**: o4-mini (always on)
|
|
134
|
+
|
|
135
|
+
See the [Reasoning Mode](#reasoning-mode) section for usage details.
|
|
136
|
+
|
|
114
137
|
## Advanced Usage
|
|
115
138
|
|
|
116
139
|
### Custom Settings
|
|
@@ -129,6 +152,163 @@ const response = await llmService.sendMessage({
|
|
|
129
152
|
});
|
|
130
153
|
```
|
|
131
154
|
|
|
155
|
+
### Reasoning Mode
|
|
156
|
+
|
|
157
|
+
Enable advanced reasoning capabilities for supported models to get step-by-step thinking and improved problem-solving:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
// Enable reasoning with automatic token budget
|
|
161
|
+
const response = await llmService.sendMessage({
|
|
162
|
+
providerId: 'gemini',
|
|
163
|
+
modelId: 'gemini-2.5-flash',
|
|
164
|
+
messages: [{ role: 'user', content: 'Solve this step by step: If a train travels 120km in 2 hours, what is its speed in m/s?' }],
|
|
165
|
+
settings: {
|
|
166
|
+
reasoning: {
|
|
167
|
+
enabled: true // Let the model decide how much thinking to do
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Use effort levels for quick control
|
|
173
|
+
const response = await llmService.sendMessage({
|
|
174
|
+
providerId: 'anthropic',
|
|
175
|
+
modelId: 'claude-3-7-sonnet-20250219',
|
|
176
|
+
messages: [{ role: 'user', content: 'Analyze this complex problem...' }],
|
|
177
|
+
settings: {
|
|
178
|
+
reasoning: {
|
|
179
|
+
enabled: true,
|
|
180
|
+
effort: 'high' // 'low', 'medium', or 'high'
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Set specific token budget for reasoning
|
|
186
|
+
const response = await llmService.sendMessage({
|
|
187
|
+
providerId: 'gemini',
|
|
188
|
+
modelId: 'gemini-2.5-flash-lite-preview-06-17',
|
|
189
|
+
messages: [{ role: 'user', content: 'What is the square root of 144?' }],
|
|
190
|
+
settings: {
|
|
191
|
+
reasoning: {
|
|
192
|
+
enabled: true,
|
|
193
|
+
maxTokens: 5000 // Specific token budget for reasoning
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Access reasoning output (if available)
|
|
199
|
+
if (response.object === 'chat.completion' && response.choices[0].reasoning) {
|
|
200
|
+
console.log('Model reasoning:', response.choices[0].reasoning);
|
|
201
|
+
console.log('Final answer:', response.choices[0].message.content);
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Reasoning Options:**
|
|
206
|
+
- `enabled`: Turn reasoning on/off (some models like o4-mini and Gemini 2.5 Pro have it always on)
|
|
207
|
+
- `effort`: Quick presets - 'low' (20% budget), 'medium' (50%), 'high' (80%)
|
|
208
|
+
- `maxTokens`: Specific token budget for reasoning
|
|
209
|
+
- `exclude`: Set to `true` to enable reasoning but exclude it from the response
|
|
210
|
+
|
|
211
|
+
**Important Notes:**
|
|
212
|
+
- Reasoning tokens are billed separately and may cost more
|
|
213
|
+
- Some models (o4-mini, Gemini 2.5 Pro) cannot disable reasoning
|
|
214
|
+
- Not all models support reasoning - check the [supported models](#models-with-reasoning-support) list
|
|
215
|
+
- The `reasoning` field in the response contains the model's thought process (when available)
|
|
216
|
+
|
|
217
|
+
### Automatic Thinking Extraction
|
|
218
|
+
|
|
219
|
+
genai-lite can capture reasoning from any model by automatically extracting content wrapped in XML tags. When models output their thinking process in tags like `<thinking>`, the library automatically moves this content to the standardized `reasoning` field. This works with all models, providing a consistent interface for accessing model reasoning:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
// Prompt the model to think step-by-step in a <thinking> tag
|
|
223
|
+
const response = await llmService.sendMessage({
|
|
224
|
+
providerId: 'openai',
|
|
225
|
+
modelId: 'gpt-4.1',
|
|
226
|
+
messages: [{
|
|
227
|
+
role: 'system',
|
|
228
|
+
content: 'When solving problems, first write your reasoning inside <thinking> tags, then provide the answer.'
|
|
229
|
+
}, {
|
|
230
|
+
role: 'user',
|
|
231
|
+
content: 'Please think through this problem step by step before answering: What is 15% of 240?'
|
|
232
|
+
}],
|
|
233
|
+
settings: {
|
|
234
|
+
thinkingExtraction: { enabled: true } // Must explicitly enable
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// If the model responds with:
|
|
239
|
+
// "<thinking>15% means 15/100 = 0.15. So 15% of 240 = 0.15 × 240 = 36.</thinking>The answer is 36."
|
|
240
|
+
//
|
|
241
|
+
// The response will have:
|
|
242
|
+
// - response.choices[0].message.content = "The answer is 36."
|
|
243
|
+
// - response.choices[0].reasoning = "<!-- Extracted by genai-lite from <thinking> tag -->\n15% means 15/100 = 0.15. So 15% of 240 = 0.15 × 240 = 36."
|
|
244
|
+
|
|
245
|
+
// If the model doesn't include the <thinking> tag, you'll get an error (with default 'auto' mode)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Configuration Options:**
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
const response = await llmService.sendMessage({
|
|
252
|
+
providerId: 'anthropic',
|
|
253
|
+
modelId: 'claude-3-5-haiku-20241022',
|
|
254
|
+
messages: [{ role: 'user', content: 'Solve this step by step...' }],
|
|
255
|
+
settings: {
|
|
256
|
+
thinkingExtraction: {
|
|
257
|
+
enabled: true, // Must explicitly enable (default: false)
|
|
258
|
+
tag: 'scratchpad', // Custom tag name (default: 'thinking')
|
|
259
|
+
onMissing: 'auto' // Smart enforcement (see below)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**The `onMissing` Property:**
|
|
266
|
+
|
|
267
|
+
The `onMissing` property controls what happens when the expected thinking tag is not found:
|
|
268
|
+
|
|
269
|
+
- `'ignore'`: Silently continue without the tag
|
|
270
|
+
- `'warn'`: Log a warning but continue processing
|
|
271
|
+
- `'error'`: Return an error response
|
|
272
|
+
- `'auto'` (default): Intelligently decide based on the model's native reasoning capabilities
|
|
273
|
+
|
|
274
|
+
**How `'auto'` Mode Works:**
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// With non-native reasoning models (e.g., GPT-4)
|
|
278
|
+
const response = await llmService.sendMessage({
|
|
279
|
+
providerId: 'openai',
|
|
280
|
+
modelId: 'gpt-4.1',
|
|
281
|
+
messages: [{
|
|
282
|
+
role: 'system',
|
|
283
|
+
content: 'Always think in <thinking> tags before answering.'
|
|
284
|
+
}, {
|
|
285
|
+
role: 'user',
|
|
286
|
+
content: 'What is 15% of 240?'
|
|
287
|
+
}],
|
|
288
|
+
settings: {
|
|
289
|
+
thinkingExtraction: { enabled: true } // onMissing: 'auto' is default
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
// Result: ERROR if <thinking> tag is missing (strict enforcement)
|
|
293
|
+
|
|
294
|
+
// With native reasoning models (e.g., Claude with reasoning enabled)
|
|
295
|
+
const response = await llmService.sendMessage({
|
|
296
|
+
providerId: 'anthropic',
|
|
297
|
+
modelId: 'claude-3-7-sonnet-20250219',
|
|
298
|
+
messages: [/* same prompt */],
|
|
299
|
+
settings: {
|
|
300
|
+
reasoning: { enabled: true },
|
|
301
|
+
thinkingExtraction: { enabled: true }
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
// Result: SUCCESS even if <thinking> tag is missing (lenient for native reasoning)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
This intelligent enforcement ensures that:
|
|
308
|
+
- Non-native models are held to strict requirements when instructed to use thinking tags
|
|
309
|
+
- Native reasoning models aren't penalized for using their built-in reasoning instead of tags
|
|
310
|
+
- The same prompt can work across different model types
|
|
311
|
+
|
|
132
312
|
### Provider Information
|
|
133
313
|
|
|
134
314
|
```typescript
|
|
@@ -144,22 +324,27 @@ const presets = llmService.getPresets();
|
|
|
144
324
|
|
|
145
325
|
### Model Presets
|
|
146
326
|
|
|
147
|
-
genai-lite includes a
|
|
327
|
+
genai-lite includes a comprehensive set of model presets for common use cases. You can use these defaults, extend them with your own, or replace them entirely.
|
|
148
328
|
|
|
149
329
|
#### Using Default Presets
|
|
150
330
|
|
|
331
|
+
The library ships with over 20 pre-configured presets (defined in `src/config/presets.json`), including specialized "thinking" presets for models with reasoning capabilities:
|
|
332
|
+
|
|
151
333
|
```typescript
|
|
152
334
|
const llmService = new LLMService(fromEnvironment);
|
|
153
335
|
|
|
154
336
|
// Get all default presets
|
|
155
337
|
const presets = llmService.getPresets();
|
|
156
338
|
// Returns presets like:
|
|
157
|
-
// - anthropic-claude-
|
|
339
|
+
// - anthropic-claude-sonnet-4-20250514-default
|
|
340
|
+
// - anthropic-claude-sonnet-4-20250514-thinking (reasoning enabled)
|
|
158
341
|
// - openai-gpt-4.1-default
|
|
159
|
-
// - google-gemini-2.5-
|
|
160
|
-
// ... and more
|
|
342
|
+
// - google-gemini-2.5-flash-thinking (reasoning enabled)
|
|
343
|
+
// ... and many more
|
|
161
344
|
```
|
|
162
345
|
|
|
346
|
+
The thinking presets automatically enable reasoning mode for supported models, making it easy to leverage advanced problem-solving capabilities without manual configuration.
|
|
347
|
+
|
|
163
348
|
#### Extending Default Presets
|
|
164
349
|
|
|
165
350
|
```typescript
|
|
@@ -213,6 +398,243 @@ const llmService = new LLMService(fromEnvironment, {
|
|
|
213
398
|
});
|
|
214
399
|
```
|
|
215
400
|
|
|
401
|
+
### Using Presets with Messages
|
|
402
|
+
|
|
403
|
+
You can use presets directly in `sendMessage` calls:
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
// Send a message using a preset
|
|
407
|
+
const response = await llmService.sendMessage({
|
|
408
|
+
presetId: 'anthropic-claude-3-7-sonnet-20250219-thinking',
|
|
409
|
+
messages: [{ role: 'user', content: 'Solve this complex problem...' }]
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// Override preset settings
|
|
413
|
+
const response = await llmService.sendMessage({
|
|
414
|
+
presetId: 'openai-gpt-4.1-default',
|
|
415
|
+
messages: [{ role: 'user', content: 'Write a story' }],
|
|
416
|
+
settings: {
|
|
417
|
+
temperature: 0.9, // Override preset's temperature
|
|
418
|
+
maxTokens: 3000
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Creating Messages from Templates
|
|
424
|
+
|
|
425
|
+
The library provides a powerful `createMessages` method that combines template rendering, model context injection, and role tag parsing into a single, intuitive API:
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
// Basic example: Create model-aware messages
|
|
429
|
+
const { messages, modelContext } = await llmService.createMessages({
|
|
430
|
+
template: `
|
|
431
|
+
<SYSTEM>
|
|
432
|
+
You are a {{ thinking_enabled ? "thoughtful" : "helpful" }} assistant.
|
|
433
|
+
{{ thinking_available && !thinking_enabled ? "Note: Reasoning mode is available for complex problems." : "" }}
|
|
434
|
+
</SYSTEM>
|
|
435
|
+
<USER>{{ question }}</USER>
|
|
436
|
+
`,
|
|
437
|
+
variables: {
|
|
438
|
+
question: 'What is the optimal algorithm for finding the shortest path in a weighted graph?'
|
|
439
|
+
},
|
|
440
|
+
presetId: 'anthropic-claude-3-7-sonnet-20250219-thinking'
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
// The messages are ready to send
|
|
444
|
+
const response = await llmService.sendMessage({
|
|
445
|
+
presetId: 'anthropic-claude-3-7-sonnet-20250219-thinking',
|
|
446
|
+
messages: messages
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
// Advanced example: Conditional context and multi-turn conversation
|
|
450
|
+
const { messages } = await llmService.createMessages({
|
|
451
|
+
template: `
|
|
452
|
+
<SYSTEM>You are an expert code reviewer.</SYSTEM>
|
|
453
|
+
{{ hasContext ? '<USER>Context: {{context}}</USER>' : '' }}
|
|
454
|
+
<USER>Review this code:
|
|
455
|
+
```{{language}}
|
|
456
|
+
{{code}}
|
|
457
|
+
```</USER>
|
|
458
|
+
{{ hasExamples ? examples : '' }}
|
|
459
|
+
<USER>Focus on {{ focusAreas.join(', ') }}.</USER>
|
|
460
|
+
`,
|
|
461
|
+
variables: {
|
|
462
|
+
hasContext: true,
|
|
463
|
+
context: 'This is part of a high-performance web server',
|
|
464
|
+
language: 'typescript',
|
|
465
|
+
code: 'async function handleRequest(req: Request) { ... }',
|
|
466
|
+
hasExamples: true,
|
|
467
|
+
examples: '<ASSISTANT>I\'ll review your code focusing on the areas you mentioned.</ASSISTANT>',
|
|
468
|
+
focusAreas: ['error handling', 'performance', 'type safety']
|
|
469
|
+
},
|
|
470
|
+
providerId: 'openai',
|
|
471
|
+
modelId: 'gpt-4.1'
|
|
472
|
+
});
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
The method provides:
|
|
476
|
+
- **Unified API**: Single method for all prompt creation needs
|
|
477
|
+
- **Model Context Injection**: Automatically injects model-specific variables
|
|
478
|
+
- **Template Rendering**: Full support for conditionals and variable substitution
|
|
479
|
+
- **Role Tag Parsing**: Converts `<SYSTEM>`, `<USER>`, and `<ASSISTANT>` tags to messages
|
|
480
|
+
|
|
481
|
+
Available model context variables:
|
|
482
|
+
- `thinking_enabled`: Whether reasoning/thinking is enabled for this request
|
|
483
|
+
- `thinking_available`: Whether the model supports reasoning/thinking
|
|
484
|
+
- `model_id`: The resolved model ID
|
|
485
|
+
- `provider_id`: The resolved provider ID
|
|
486
|
+
- `reasoning_effort`: The reasoning effort level if specified
|
|
487
|
+
- `reasoning_max_tokens`: The reasoning token budget if specified
|
|
488
|
+
|
|
489
|
+
#### Advanced Features
|
|
490
|
+
|
|
491
|
+
**Dynamic Role Injection:**
|
|
492
|
+
Variables can dynamically inject entire role blocks, enabling flexible conversation flows:
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
const { messages } = await llmService.createMessages({
|
|
496
|
+
template: `
|
|
497
|
+
{{ includeSystemPrompt ? '<SYSTEM>{{systemPrompt}}</SYSTEM>' : '' }}
|
|
498
|
+
{{ examples ? examples : '' }}
|
|
499
|
+
<USER>{{userQuery}}</USER>
|
|
500
|
+
`,
|
|
501
|
+
variables: {
|
|
502
|
+
includeSystemPrompt: true,
|
|
503
|
+
systemPrompt: 'You are an expert code reviewer.',
|
|
504
|
+
examples: `
|
|
505
|
+
<USER>Review this code: const x = 1</USER>
|
|
506
|
+
<ASSISTANT>The variable name 'x' is not descriptive...</ASSISTANT>
|
|
507
|
+
`,
|
|
508
|
+
userQuery: 'Review this: const data = fetchData()'
|
|
509
|
+
},
|
|
510
|
+
presetId: 'anthropic-claude-3-5-sonnet-20241022'
|
|
511
|
+
});
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
**Combining with Thinking Extraction:**
|
|
515
|
+
When using models without native reasoning support, combine createMessages with thinking extraction:
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
// Prompt any model to think before answering
|
|
519
|
+
const { messages } = await llmService.createMessages({
|
|
520
|
+
template: `
|
|
521
|
+
<SYSTEM>
|
|
522
|
+
When solving problems, first write your step-by-step reasoning inside <thinking> tags,
|
|
523
|
+
then provide your final answer.
|
|
524
|
+
</SYSTEM>
|
|
525
|
+
<USER>{{ question }}</USER>
|
|
526
|
+
`,
|
|
527
|
+
variables: { question: 'If a train travels 120km in 2 hours, what is its speed in m/s?' },
|
|
528
|
+
providerId: 'openai',
|
|
529
|
+
modelId: 'gpt-4.1'
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// Send with automatic thinking extraction
|
|
533
|
+
const response = await llmService.sendMessage({
|
|
534
|
+
providerId: 'openai',
|
|
535
|
+
modelId: 'gpt-4.1',
|
|
536
|
+
messages,
|
|
537
|
+
settings: {
|
|
538
|
+
thinkingExtraction: { enabled: true } // Default, but shown for clarity
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
// Access both reasoning and answer
|
|
543
|
+
if (response.object === 'chat.completion') {
|
|
544
|
+
console.log('Reasoning:', response.choices[0].reasoning);
|
|
545
|
+
console.log('Answer:', response.choices[0].message.content);
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### Self-Contained Templates with Metadata
|
|
550
|
+
|
|
551
|
+
Templates can now include their own settings using a `<META>` block, making them truly self-contained and reusable:
|
|
552
|
+
|
|
553
|
+
```typescript
|
|
554
|
+
// Define a template with embedded settings
|
|
555
|
+
const creativeWritingTemplate = `
|
|
556
|
+
<META>
|
|
557
|
+
{
|
|
558
|
+
"settings": {
|
|
559
|
+
"temperature": 0.9,
|
|
560
|
+
"maxTokens": 3000,
|
|
561
|
+
"thinkingExtraction": { "enabled": true, "tag": "reasoning" }
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
</META>
|
|
565
|
+
<SYSTEM>
|
|
566
|
+
You are a creative writer. Use <reasoning> tags to outline your story structure
|
|
567
|
+
before writing the actual story.
|
|
568
|
+
</SYSTEM>
|
|
569
|
+
<USER>Write a short story about {{ topic }}</USER>
|
|
570
|
+
`;
|
|
571
|
+
|
|
572
|
+
// Use the template - settings are automatically extracted
|
|
573
|
+
const { messages, settings } = await llmService.createMessages({
|
|
574
|
+
template: creativeWritingTemplate,
|
|
575
|
+
variables: { topic: 'a robot discovering music' },
|
|
576
|
+
providerId: 'openai',
|
|
577
|
+
modelId: 'gpt-4.1'
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
// Send the message with the template's settings
|
|
581
|
+
const response = await llmService.sendMessage({
|
|
582
|
+
providerId: 'openai',
|
|
583
|
+
modelId: 'gpt-4.1',
|
|
584
|
+
messages,
|
|
585
|
+
settings // Uses temperature: 0.9, maxTokens: 3000, etc.
|
|
586
|
+
});
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
**Benefits of Self-Contained Templates:**
|
|
590
|
+
- **Portability**: Templates carry their optimal settings with them
|
|
591
|
+
- **Consistency**: Same template always uses the same settings
|
|
592
|
+
- **Less Error-Prone**: No need to remember settings for each template
|
|
593
|
+
- **Shareable**: Easy to share templates with all necessary configuration
|
|
594
|
+
|
|
595
|
+
**Settings Hierarchy:**
|
|
596
|
+
When multiple settings sources exist, they are merged in this order (later overrides earlier):
|
|
597
|
+
1. Model defaults (lowest priority)
|
|
598
|
+
2. Preset settings
|
|
599
|
+
3. Template `<META>` settings
|
|
600
|
+
4. Runtime settings in `sendMessage()` (highest priority)
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
// Example of settings hierarchy
|
|
604
|
+
const { messages, settings: templateSettings } = await llmService.createMessages({
|
|
605
|
+
template: `<META>{"settings": {"temperature": 0.8}}</META><USER>Hello</USER>`,
|
|
606
|
+
presetId: 'some-preset' // Preset might have temperature: 0.7
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// Final temperature will be 0.9 (runtime overrides all)
|
|
610
|
+
const response = await llmService.sendMessage({
|
|
611
|
+
presetId: 'some-preset',
|
|
612
|
+
messages,
|
|
613
|
+
settings: {
|
|
614
|
+
...templateSettings,
|
|
615
|
+
temperature: 0.9 // Runtime override
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
**Validation:**
|
|
621
|
+
Invalid settings in the `<META>` block are logged as warnings and ignored:
|
|
622
|
+
|
|
623
|
+
```typescript
|
|
624
|
+
const template = `
|
|
625
|
+
<META>
|
|
626
|
+
{
|
|
627
|
+
"settings": {
|
|
628
|
+
"temperature": 3.0, // Invalid: will be ignored with warning
|
|
629
|
+
"maxTokens": 2000, // Valid: will be used
|
|
630
|
+
"unknownSetting": "foo" // Unknown: will be ignored with warning
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
</META>
|
|
634
|
+
<USER>Test</USER>
|
|
635
|
+
`;
|
|
636
|
+
```
|
|
637
|
+
|
|
216
638
|
### Error Handling
|
|
217
639
|
|
|
218
640
|
```typescript
|
|
@@ -284,13 +706,19 @@ genai-lite is written in TypeScript and provides comprehensive type definitions:
|
|
|
284
706
|
```typescript
|
|
285
707
|
import type {
|
|
286
708
|
LLMChatRequest,
|
|
709
|
+
LLMChatRequestWithPreset,
|
|
287
710
|
LLMResponse,
|
|
288
711
|
LLMFailureResponse,
|
|
289
712
|
LLMSettings,
|
|
713
|
+
LLMReasoningSettings,
|
|
714
|
+
LLMThinkingExtractionSettings,
|
|
290
715
|
ApiKeyProvider,
|
|
291
716
|
ModelPreset,
|
|
292
717
|
LLMServiceOptions,
|
|
293
|
-
PresetMode
|
|
718
|
+
PresetMode,
|
|
719
|
+
ModelContext,
|
|
720
|
+
CreateMessagesResult,
|
|
721
|
+
TemplateMetadata
|
|
294
722
|
} from 'genai-lite';
|
|
295
723
|
```
|
|
296
724
|
|
|
@@ -412,6 +840,19 @@ const prompt = renderTemplate(
|
|
|
412
840
|
);
|
|
413
841
|
// Result includes the context line when hasContext is true
|
|
414
842
|
|
|
843
|
+
// Using logical operators in conditions
|
|
844
|
+
const accessControl = renderTemplate(
|
|
845
|
+
'{{ isAuthenticated && !isBanned ? `Welcome {{username}}!` : `Access denied` }}',
|
|
846
|
+
{ isAuthenticated: true, isBanned: false, username: 'Alice' }
|
|
847
|
+
);
|
|
848
|
+
// Result: "Welcome Alice!"
|
|
849
|
+
|
|
850
|
+
const notification = renderTemplate(
|
|
851
|
+
'{{ hasEmail || hasPhone ? `Contact info available` : `No contact info` }}',
|
|
852
|
+
{ hasEmail: false, hasPhone: true }
|
|
853
|
+
);
|
|
854
|
+
// Result: "Contact info available"
|
|
855
|
+
|
|
415
856
|
// Complex template with multiple conditionals
|
|
416
857
|
const complexTemplate = `
|
|
417
858
|
System: You are a {{ role }} assistant.
|
|
@@ -440,10 +881,17 @@ const result = renderTemplate(complexTemplate, {
|
|
|
440
881
|
Template syntax supports:
|
|
441
882
|
- **Simple substitution**: `{{ variableName }}`
|
|
442
883
|
- **Ternary conditionals**: `{{ condition ? `true result` : `false result` }}`
|
|
884
|
+
- **Logical operators in conditions**:
|
|
885
|
+
- NOT: `{{ !isDisabled ? `enabled` : `disabled` }}`
|
|
886
|
+
- AND: `{{ hasPermission && isActive ? `show` : `hide` }}`
|
|
887
|
+
- OR: `{{ isAdmin || isOwner ? `allow` : `deny` }}`
|
|
888
|
+
- Combined: `{{ !isDraft && isPublished ? `visible` : `hidden` }}`
|
|
443
889
|
- **Nested variables**: `{{ show ? `Name: {{name}}` : `Anonymous` }}`
|
|
444
890
|
- **Multi-line strings**: Use backticks to preserve formatting
|
|
445
891
|
- **Intelligent newline handling**: Empty results remove trailing newlines
|
|
446
892
|
|
|
893
|
+
Note: Logical operators support up to 2 operands and don't support parentheses or mixing && and ||.
|
|
894
|
+
|
|
447
895
|
### Example: Building Dynamic LLM Prompts
|
|
448
896
|
|
|
449
897
|
Combine the template engine with other utilities for powerful prompt generation:
|
|
@@ -491,36 +939,65 @@ const response = await llm.sendMessage({
|
|
|
491
939
|
});
|
|
492
940
|
```
|
|
493
941
|
|
|
494
|
-
### Prompt
|
|
942
|
+
### Prompt Engineering Utilities
|
|
495
943
|
|
|
496
|
-
genai-lite provides powerful utilities for
|
|
944
|
+
genai-lite provides powerful utilities for working with prompts and responses:
|
|
497
945
|
|
|
498
|
-
####
|
|
946
|
+
#### Creating Messages from Templates
|
|
499
947
|
|
|
500
|
-
|
|
948
|
+
The recommended way to create messages is using `LLMService.createMessages`, which provides a unified API for template rendering, model context injection, and role tag parsing:
|
|
501
949
|
|
|
502
950
|
```typescript
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
<SYSTEM>You are a helpful assistant specialized in {{expertise}}.</SYSTEM>
|
|
507
|
-
<USER>Help me with {{task}}</USER>
|
|
508
|
-
<ASSISTANT>I'll help you with {{task}}. Let me analyze the requirements...</ASSISTANT>
|
|
509
|
-
<USER>Can you provide more details?</USER>
|
|
510
|
-
|
|
951
|
+
// Basic multi-turn conversation
|
|
952
|
+
const { messages } = await llmService.createMessages({
|
|
953
|
+
template: `
|
|
954
|
+
<SYSTEM>You are a helpful assistant specialized in {{expertise}}.</SYSTEM>
|
|
955
|
+
<USER>Help me with {{task}}</USER>
|
|
956
|
+
<ASSISTANT>I'll help you with {{task}}. Let me analyze the requirements...</ASSISTANT>
|
|
957
|
+
<USER>Can you provide more details?</USER>
|
|
958
|
+
`,
|
|
959
|
+
variables: {
|
|
960
|
+
expertise: 'TypeScript and React',
|
|
961
|
+
task: 'building a custom hook'
|
|
962
|
+
},
|
|
963
|
+
presetId: 'openai-gpt-4.1-default' // Optional: adds model context
|
|
964
|
+
});
|
|
511
965
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
966
|
+
// Advanced: Leverage model context for adaptive prompts
|
|
967
|
+
const { messages, modelContext } = await llmService.createMessages({
|
|
968
|
+
template: `
|
|
969
|
+
<SYSTEM>
|
|
970
|
+
You are a {{ thinking_enabled ? 'analytical problem solver' : 'quick helper' }}.
|
|
971
|
+
{{ model_id.includes('claude') ? 'Use your advanced reasoning capabilities.' : '' }}
|
|
972
|
+
</SYSTEM>
|
|
973
|
+
<USER>
|
|
974
|
+
{{ thinking_enabled ? 'Please solve this step-by-step:' : 'Please answer:' }}
|
|
975
|
+
{{ question }}
|
|
976
|
+
</USER>
|
|
977
|
+
`,
|
|
978
|
+
variables: { question: 'What causes the seasons on Earth?' },
|
|
979
|
+
presetId: 'anthropic-claude-3-7-sonnet-20250219-thinking'
|
|
515
980
|
});
|
|
516
981
|
|
|
517
|
-
|
|
518
|
-
//
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
982
|
+
console.log('Model context:', modelContext);
|
|
983
|
+
// Output: { thinking_enabled: true, thinking_available: true, model_id: 'claude-3-7-sonnet-20250219', ... }
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
**Low-Level Utilities:**
|
|
987
|
+
For cases where you need template parsing without model context:
|
|
988
|
+
|
|
989
|
+
```typescript
|
|
990
|
+
import { parseRoleTags, renderTemplate } from 'genai-lite/prompting';
|
|
991
|
+
|
|
992
|
+
// Render variables first
|
|
993
|
+
const rendered = renderTemplate(
|
|
994
|
+
'<SYSTEM>You are a {{role}} assistant.</SYSTEM><USER>{{query}}</USER>',
|
|
995
|
+
{ role: 'helpful', query: 'What is TypeScript?' }
|
|
996
|
+
);
|
|
997
|
+
|
|
998
|
+
// Then parse role tags
|
|
999
|
+
const messages = parseRoleTags(rendered);
|
|
1000
|
+
// Result: [{ role: 'system', content: 'You are a helpful assistant.' }, { role: 'user', content: 'What is TypeScript?' }]
|
|
524
1001
|
```
|
|
525
1002
|
|
|
526
1003
|
#### Extracting Random Variables for Few-Shot Learning
|
|
@@ -616,10 +1093,11 @@ console.log(parsed.SUGGESTIONS); // The suggestions text
|
|
|
616
1093
|
console.log(parsed.REFACTORED_CODE); // The refactored code
|
|
617
1094
|
```
|
|
618
1095
|
|
|
619
|
-
These
|
|
620
|
-
- **Structured Conversations**: Build multi-turn conversations from templates
|
|
1096
|
+
These utilities enable:
|
|
1097
|
+
- **Structured Conversations**: Build multi-turn conversations from templates with model context awareness
|
|
621
1098
|
- **Few-Shot Learning**: Randomly sample examples to improve AI responses
|
|
622
1099
|
- **Reliable Output Parsing**: Extract specific sections from AI responses
|
|
1100
|
+
- **Automatic Thinking Extraction**: Capture reasoning from any model using XML tags
|
|
623
1101
|
- **Template Reusability**: Define templates once, use with different variables
|
|
624
1102
|
- **Type Safety**: Full TypeScript support with LLMMessage types
|
|
625
1103
|
|