genai-lite 0.2.1 → 0.3.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 +382 -49
- 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 +48 -83
- package/dist/llm/LLMService.js +176 -480
- package/dist/llm/LLMService.original.d.ts +147 -0
- package/dist/llm/LLMService.original.js +656 -0
- package/dist/llm/LLMService.test.js +187 -0
- package/dist/llm/clients/AnthropicClientAdapter.test.js +4 -0
- package/dist/llm/clients/GeminiClientAdapter.test.js +4 -0
- package/dist/llm/clients/MockClientAdapter.js +29 -13
- package/dist/llm/clients/MockClientAdapter.test.js +4 -0
- package/dist/llm/clients/OpenAIClientAdapter.test.js +4 -0
- package/dist/llm/config.js +5 -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 +29 -28
- 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 +1 -1
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 {
|
|
@@ -201,6 +214,101 @@ if (response.object === 'chat.completion' && response.choices[0].reasoning) {
|
|
|
201
214
|
- Not all models support reasoning - check the [supported models](#models-with-reasoning-support) list
|
|
202
215
|
- The `reasoning` field in the response contains the model's thought process (when available)
|
|
203
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 = "15% 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
|
+
|
|
204
312
|
### Provider Information
|
|
205
313
|
|
|
206
314
|
```typescript
|
|
@@ -312,40 +420,65 @@ const response = await llmService.sendMessage({
|
|
|
312
420
|
});
|
|
313
421
|
```
|
|
314
422
|
|
|
315
|
-
###
|
|
423
|
+
### Creating Messages from Templates
|
|
316
424
|
|
|
317
|
-
The library provides a powerful `
|
|
425
|
+
The library provides a powerful `createMessages` method that combines template rendering, model context injection, and role tag parsing into a single, intuitive API:
|
|
318
426
|
|
|
319
427
|
```typescript
|
|
320
|
-
//
|
|
321
|
-
const
|
|
428
|
+
// Basic example: Create model-aware messages
|
|
429
|
+
const { messages, modelContext } = await llmService.createMessages({
|
|
322
430
|
template: `
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
{{
|
|
326
|
-
|
|
327
|
-
{{
|
|
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>
|
|
328
436
|
`,
|
|
329
437
|
variables: {
|
|
330
|
-
question: 'What is the optimal algorithm for finding the shortest path in a weighted graph?'
|
|
438
|
+
question: 'What is the optimal algorithm for finding the shortest path in a weighted graph?'
|
|
331
439
|
},
|
|
332
440
|
presetId: 'anthropic-claude-3-7-sonnet-20250219-thinking'
|
|
333
441
|
});
|
|
334
442
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
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
|
+
});
|
|
346
473
|
```
|
|
347
474
|
|
|
348
|
-
The
|
|
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:
|
|
349
482
|
- `thinking_enabled`: Whether reasoning/thinking is enabled for this request
|
|
350
483
|
- `thinking_available`: Whether the model supports reasoning/thinking
|
|
351
484
|
- `model_id`: The resolved model ID
|
|
@@ -353,6 +486,155 @@ The model context includes:
|
|
|
353
486
|
- `reasoning_effort`: The reasoning effort level if specified
|
|
354
487
|
- `reasoning_max_tokens`: The reasoning token budget if specified
|
|
355
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
|
+
|
|
356
638
|
### Error Handling
|
|
357
639
|
|
|
358
640
|
```typescript
|
|
@@ -429,13 +711,14 @@ import type {
|
|
|
429
711
|
LLMFailureResponse,
|
|
430
712
|
LLMSettings,
|
|
431
713
|
LLMReasoningSettings,
|
|
714
|
+
LLMThinkingExtractionSettings,
|
|
432
715
|
ApiKeyProvider,
|
|
433
716
|
ModelPreset,
|
|
434
717
|
LLMServiceOptions,
|
|
435
718
|
PresetMode,
|
|
436
|
-
PrepareMessageOptions,
|
|
437
719
|
ModelContext,
|
|
438
|
-
|
|
720
|
+
CreateMessagesResult,
|
|
721
|
+
TemplateMetadata
|
|
439
722
|
} from 'genai-lite';
|
|
440
723
|
```
|
|
441
724
|
|
|
@@ -557,6 +840,19 @@ const prompt = renderTemplate(
|
|
|
557
840
|
);
|
|
558
841
|
// Result includes the context line when hasContext is true
|
|
559
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
|
+
|
|
560
856
|
// Complex template with multiple conditionals
|
|
561
857
|
const complexTemplate = `
|
|
562
858
|
System: You are a {{ role }} assistant.
|
|
@@ -585,10 +881,17 @@ const result = renderTemplate(complexTemplate, {
|
|
|
585
881
|
Template syntax supports:
|
|
586
882
|
- **Simple substitution**: `{{ variableName }}`
|
|
587
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` }}`
|
|
588
889
|
- **Nested variables**: `{{ show ? `Name: {{name}}` : `Anonymous` }}`
|
|
589
890
|
- **Multi-line strings**: Use backticks to preserve formatting
|
|
590
891
|
- **Intelligent newline handling**: Empty results remove trailing newlines
|
|
591
892
|
|
|
893
|
+
Note: Logical operators support up to 2 operands and don't support parentheses or mixing && and ||.
|
|
894
|
+
|
|
592
895
|
### Example: Building Dynamic LLM Prompts
|
|
593
896
|
|
|
594
897
|
Combine the template engine with other utilities for powerful prompt generation:
|
|
@@ -636,36 +939,65 @@ const response = await llm.sendMessage({
|
|
|
636
939
|
});
|
|
637
940
|
```
|
|
638
941
|
|
|
639
|
-
### Prompt
|
|
942
|
+
### Prompt Engineering Utilities
|
|
640
943
|
|
|
641
|
-
genai-lite provides powerful utilities for
|
|
944
|
+
genai-lite provides powerful utilities for working with prompts and responses:
|
|
642
945
|
|
|
643
|
-
####
|
|
946
|
+
#### Creating Messages from Templates
|
|
644
947
|
|
|
645
|
-
|
|
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:
|
|
646
949
|
|
|
647
950
|
```typescript
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
<SYSTEM>You are a helpful assistant specialized in {{expertise}}.</SYSTEM>
|
|
652
|
-
<USER>Help me with {{task}}</USER>
|
|
653
|
-
<ASSISTANT>I'll help you with {{task}}. Let me analyze the requirements...</ASSISTANT>
|
|
654
|
-
<USER>Can you provide more details?</USER>
|
|
655
|
-
|
|
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
|
+
});
|
|
656
965
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
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'
|
|
660
980
|
});
|
|
661
981
|
|
|
662
|
-
|
|
663
|
-
//
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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?' }]
|
|
669
1001
|
```
|
|
670
1002
|
|
|
671
1003
|
#### Extracting Random Variables for Few-Shot Learning
|
|
@@ -761,10 +1093,11 @@ console.log(parsed.SUGGESTIONS); // The suggestions text
|
|
|
761
1093
|
console.log(parsed.REFACTORED_CODE); // The refactored code
|
|
762
1094
|
```
|
|
763
1095
|
|
|
764
|
-
These
|
|
765
|
-
- **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
|
|
766
1098
|
- **Few-Shot Learning**: Randomly sample examples to improve AI responses
|
|
767
1099
|
- **Reliable Output Parsing**: Extract specific sections from AI responses
|
|
1100
|
+
- **Automatic Thinking Extraction**: Capture reasoning from any model using XML tags
|
|
768
1101
|
- **Template Reusability**: Define templates once, use with different variables
|
|
769
1102
|
- **Type Safety**: Full TypeScript support with LLMMessage types
|
|
770
1103
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export type { ApiKeyProvider } from "./types";
|
|
2
2
|
export { LLMService } from "./llm/LLMService";
|
|
3
|
-
export type { LLMServiceOptions, PresetMode } from "./llm/LLMService";
|
|
3
|
+
export type { LLMServiceOptions, PresetMode, CreateMessagesResult } from "./llm/LLMService";
|
|
4
4
|
export type { ModelPreset } from "./types/presets";
|
|
5
5
|
export * from "./llm/types";
|
|
6
6
|
export * from "./llm/clients/types";
|
|
7
7
|
export { fromEnvironment } from "./providers/fromEnvironment";
|
|
8
8
|
export { renderTemplate } from "./prompting/template";
|
|
9
9
|
export { countTokens, getSmartPreview, extractRandomVariables } from "./prompting/content";
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
10
|
+
export { parseStructuredContent, parseRoleTags, extractInitialTaggedContent, parseTemplateWithMetadata } from "./prompting/parser";
|
|
11
|
+
export type { TemplateMetadata } from "./prompting/parser";
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.
|
|
17
|
+
exports.parseTemplateWithMetadata = exports.extractInitialTaggedContent = exports.parseRoleTags = exports.parseStructuredContent = exports.extractRandomVariables = exports.getSmartPreview = exports.countTokens = exports.renderTemplate = exports.fromEnvironment = exports.LLMService = void 0;
|
|
18
18
|
// --- LLM Service ---
|
|
19
19
|
var LLMService_1 = require("./llm/LLMService");
|
|
20
20
|
Object.defineProperty(exports, "LLMService", { enumerable: true, get: function () { return LLMService_1.LLMService; } });
|
|
@@ -32,7 +32,8 @@ var content_1 = require("./prompting/content");
|
|
|
32
32
|
Object.defineProperty(exports, "countTokens", { enumerable: true, get: function () { return content_1.countTokens; } });
|
|
33
33
|
Object.defineProperty(exports, "getSmartPreview", { enumerable: true, get: function () { return content_1.getSmartPreview; } });
|
|
34
34
|
Object.defineProperty(exports, "extractRandomVariables", { enumerable: true, get: function () { return content_1.extractRandomVariables; } });
|
|
35
|
-
var builder_1 = require("./prompting/builder");
|
|
36
|
-
Object.defineProperty(exports, "buildMessagesFromTemplate", { enumerable: true, get: function () { return builder_1.buildMessagesFromTemplate; } });
|
|
37
35
|
var parser_1 = require("./prompting/parser");
|
|
38
36
|
Object.defineProperty(exports, "parseStructuredContent", { enumerable: true, get: function () { return parser_1.parseStructuredContent; } });
|
|
37
|
+
Object.defineProperty(exports, "parseRoleTags", { enumerable: true, get: function () { return parser_1.parseRoleTags; } });
|
|
38
|
+
Object.defineProperty(exports, "extractInitialTaggedContent", { enumerable: true, get: function () { return parser_1.extractInitialTaggedContent; } });
|
|
39
|
+
Object.defineProperty(exports, "parseTemplateWithMetadata", { enumerable: true, get: function () { return parser_1.parseTemplateWithMetadata; } });
|