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.
Files changed (49) hide show
  1. package/README.md +382 -49
  2. package/dist/index.d.ts +3 -3
  3. package/dist/index.js +4 -3
  4. package/dist/llm/LLMService.createMessages.test.d.ts +4 -0
  5. package/dist/llm/LLMService.createMessages.test.js +364 -0
  6. package/dist/llm/LLMService.d.ts +48 -83
  7. package/dist/llm/LLMService.js +176 -480
  8. package/dist/llm/LLMService.original.d.ts +147 -0
  9. package/dist/llm/LLMService.original.js +656 -0
  10. package/dist/llm/LLMService.test.js +187 -0
  11. package/dist/llm/clients/AnthropicClientAdapter.test.js +4 -0
  12. package/dist/llm/clients/GeminiClientAdapter.test.js +4 -0
  13. package/dist/llm/clients/MockClientAdapter.js +29 -13
  14. package/dist/llm/clients/MockClientAdapter.test.js +4 -0
  15. package/dist/llm/clients/OpenAIClientAdapter.test.js +4 -0
  16. package/dist/llm/config.js +5 -0
  17. package/dist/llm/services/AdapterRegistry.d.ts +59 -0
  18. package/dist/llm/services/AdapterRegistry.js +113 -0
  19. package/dist/llm/services/AdapterRegistry.test.d.ts +1 -0
  20. package/dist/llm/services/AdapterRegistry.test.js +239 -0
  21. package/dist/llm/services/ModelResolver.d.ts +35 -0
  22. package/dist/llm/services/ModelResolver.js +116 -0
  23. package/dist/llm/services/ModelResolver.test.d.ts +1 -0
  24. package/dist/llm/services/ModelResolver.test.js +158 -0
  25. package/dist/llm/services/PresetManager.d.ts +27 -0
  26. package/dist/llm/services/PresetManager.js +50 -0
  27. package/dist/llm/services/PresetManager.test.d.ts +1 -0
  28. package/dist/llm/services/PresetManager.test.js +210 -0
  29. package/dist/llm/services/RequestValidator.d.ts +31 -0
  30. package/dist/llm/services/RequestValidator.js +122 -0
  31. package/dist/llm/services/RequestValidator.test.d.ts +1 -0
  32. package/dist/llm/services/RequestValidator.test.js +159 -0
  33. package/dist/llm/services/SettingsManager.d.ts +32 -0
  34. package/dist/llm/services/SettingsManager.js +223 -0
  35. package/dist/llm/services/SettingsManager.test.d.ts +1 -0
  36. package/dist/llm/services/SettingsManager.test.js +266 -0
  37. package/dist/llm/types.d.ts +29 -28
  38. package/dist/prompting/builder.d.ts +4 -0
  39. package/dist/prompting/builder.js +12 -61
  40. package/dist/prompting/content.js +3 -9
  41. package/dist/prompting/index.d.ts +2 -3
  42. package/dist/prompting/index.js +4 -5
  43. package/dist/prompting/parser.d.ts +80 -0
  44. package/dist/prompting/parser.js +133 -0
  45. package/dist/prompting/parser.test.js +348 -0
  46. package/dist/prompting/template.d.ts +8 -0
  47. package/dist/prompting/template.js +89 -6
  48. package/dist/prompting/template.test.js +116 -0
  49. 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
- // Send a message to OpenAI
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
- ### Model-Aware Template Rendering
423
+ ### Creating Messages from Templates
316
424
 
317
- The library provides a powerful `prepareMessage` method that renders templates with model context, allowing you to create adaptive prompts based on model capabilities:
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
- // Prepare a message with model-aware template
321
- const result = await llmService.prepareMessage({
428
+ // Basic example: Create model-aware messages
429
+ const { messages, modelContext } = await llmService.createMessages({
322
430
  template: `
323
- {{ thinking_enabled ? "Please think step-by-step about this problem:" : "Please analyze this problem:" }}
324
-
325
- {{ question }}
326
-
327
- {{ thinking_available && !thinking_enabled ? "(Note: This model supports reasoning mode which could help with complex problems)" : "" }}
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
- if (result.object !== 'error') {
336
- // Access the prepared messages and model context
337
- console.log('Messages:', result.messages);
338
- console.log('Model context:', result.modelContext);
339
-
340
- // Send the prepared messages
341
- const response = await llmService.sendMessage({
342
- presetId: 'anthropic-claude-3-7-sonnet-20250219-thinking',
343
- messages: result.messages
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 model context includes:
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
- PrepareMessageResult
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 Builder Utilities
942
+ ### Prompt Engineering Utilities
640
943
 
641
- genai-lite provides powerful utilities for building and parsing structured prompts:
944
+ genai-lite provides powerful utilities for working with prompts and responses:
642
945
 
643
- #### Parsing Messages from Templates
946
+ #### Creating Messages from Templates
644
947
 
645
- Convert template strings with role tags into LLM message arrays:
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
- import { buildMessagesFromTemplate } from 'genai-lite/prompting';
649
-
650
- const template = `
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
- const messages = buildMessagesFromTemplate(template, {
658
- expertise: 'TypeScript and React',
659
- task: 'building a custom hook'
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
- // Result: Array of LLMMessage objects ready for the API
663
- // [
664
- // { role: 'system', content: 'You are a helpful assistant specialized in TypeScript and React.' },
665
- // { role: 'user', content: 'Help me with building a custom hook' },
666
- // { role: 'assistant', content: "I'll help you with building a custom hook. Let me analyze..." },
667
- // { role: 'user', content: 'Can you provide more details?' }
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 prompt builder utilities enable:
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 { buildMessagesFromTemplate } from "./prompting/builder";
11
- export { parseStructuredContent } from "./prompting/parser";
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.parseStructuredContent = exports.buildMessagesFromTemplate = exports.extractRandomVariables = exports.getSmartPreview = exports.countTokens = exports.renderTemplate = exports.fromEnvironment = exports.LLMService = void 0;
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; } });
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Tests for LLMService.createMessages method
3
+ */
4
+ export {};