poml 0.0.6 → 0.0.7

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/docs/tutorial/advanced/performance.md +695 -0
  3. data/docs/tutorial/advanced/tool-registration.md +776 -0
  4. data/docs/tutorial/basic-usage.md +351 -0
  5. data/docs/tutorial/components/chat-components.md +552 -0
  6. data/docs/tutorial/components/formatting.md +623 -0
  7. data/docs/tutorial/components/index.md +366 -0
  8. data/docs/tutorial/components/media-components.md +259 -0
  9. data/docs/tutorial/components/schema-components.md +668 -0
  10. data/docs/tutorial/index.md +184 -0
  11. data/docs/tutorial/output-formats.md +688 -0
  12. data/docs/tutorial/quickstart.md +30 -0
  13. data/docs/tutorial/template-engine.md +540 -0
  14. data/lib/poml/components/base.rb +146 -4
  15. data/lib/poml/components/content.rb +10 -3
  16. data/lib/poml/components/data.rb +539 -19
  17. data/lib/poml/components/examples.rb +235 -1
  18. data/lib/poml/components/formatting.rb +184 -18
  19. data/lib/poml/components/layout.rb +7 -2
  20. data/lib/poml/components/lists.rb +69 -35
  21. data/lib/poml/components/meta.rb +134 -5
  22. data/lib/poml/components/output_schema.rb +19 -1
  23. data/lib/poml/components/template.rb +72 -61
  24. data/lib/poml/components/text.rb +30 -1
  25. data/lib/poml/components/tool.rb +81 -0
  26. data/lib/poml/components/tool_definition.rb +339 -10
  27. data/lib/poml/components/tools.rb +14 -0
  28. data/lib/poml/components/utilities.rb +34 -18
  29. data/lib/poml/components.rb +19 -0
  30. data/lib/poml/context.rb +19 -4
  31. data/lib/poml/parser.rb +88 -63
  32. data/lib/poml/renderer.rb +191 -9
  33. data/lib/poml/template_engine.rb +138 -13
  34. data/lib/poml/version.rb +1 -1
  35. data/lib/poml.rb +16 -1
  36. data/readme.md +154 -27
  37. metadata +31 -4
  38. data/TUTORIAL.md +0 -987
data/TUTORIAL.md DELETED
@@ -1,987 +0,0 @@
1
- # POML Ruby Gem - Comprehensive Tutorial
2
-
3
- This tutorial provides detailed examples and practical use cases for using the POML Ruby gem in your applications.
4
-
5
- ## Table of Contents
6
-
7
- - [Basic Usage](#basic-usage)
8
- - [Output Formats](#output-formats)
9
- - [Template Variables](#template-variables)
10
- - [Stylesheets](#stylesheets)
11
- - [Working with Files](#working-with-files)
12
- - [Advanced Components](#advanced-components)
13
- - [Error Handling](#error-handling)
14
- - [Integration Examples](#integration-examples)
15
- - [Best Practices](#best-practices)
16
-
17
- ## Basic Usage
18
-
19
- ### Simple Prompt Creation
20
-
21
- ```ruby
22
- require 'poml'
23
-
24
- # Basic role and task
25
- markup = '<poml><role>Assistant</role><task>Help users with questions</task></poml>'
26
- result = Poml.process(markup: markup)
27
-
28
- puts result['content']
29
- # Output: Raw text with role and task formatted
30
- ```
31
-
32
- ### Adding Hints and Context
33
-
34
- ```ruby
35
- require 'poml'
36
-
37
- markup = <<~POML
38
- <poml>
39
- <role>Expert Data Scientist</role>
40
- <task>Analyze the provided dataset and identify trends</task>
41
- <hint>Focus on seasonal patterns and anomalies</hint>
42
- <p>Please provide detailed insights with visualizations.</p>
43
- </poml>
44
- POML
45
-
46
- result = Poml.process(markup: markup)
47
- puts result['content']
48
- ```
49
-
50
- ### Using Paragraphs and Structure
51
-
52
- ```ruby
53
- require 'poml'
54
-
55
- markup = <<~POML
56
- <poml>
57
- <role>Technical Writer</role>
58
- <task>Create documentation</task>
59
-
60
- <p>Write clear and concise documentation for the following API endpoints:</p>
61
-
62
- <list>
63
- <item>GET /users - Retrieve user list</item>
64
- <item>POST /users - Create new user</item>
65
- <item>PUT /users/:id - Update user</item>
66
- <item>DELETE /users/:id - Delete user</item>
67
- </list>
68
-
69
- <p>Include examples and error handling for each endpoint.</p>
70
- </poml>
71
- POML
72
-
73
- result = Poml.process(markup: markup)
74
- puts result['content']
75
- ```
76
-
77
- ## Output Formats
78
-
79
- ### Raw Format
80
-
81
- Perfect for direct use with AI APIs or when you need plain text output:
82
-
83
- ```ruby
84
- require 'poml'
85
-
86
- markup = '<poml><role>Assistant</role><task>Summarize this text</task></poml>'
87
- result = Poml.process(markup: markup, format: 'raw')
88
-
89
- puts result
90
- # Output: Plain text with message boundaries
91
- # ===== system =====
92
- #
93
- # # Role
94
- #
95
- # Assistant
96
- #
97
- # # Task
98
- #
99
- # Summarize this text
100
- ```
101
-
102
- ### Dictionary Format (Default)
103
-
104
- Best for Ruby applications that need structured data:
105
-
106
- ```ruby
107
- require 'poml'
108
-
109
- markup = '<poml><role>Assistant</role><task>Help users</task></poml>'
110
- result = Poml.process(markup: markup, format: 'dict')
111
-
112
- puts result.class # Hash
113
- puts result['content'] # The formatted content
114
- puts result['metadata']['chat'] # true
115
- puts result['metadata']['variables'] # {}
116
- ```
117
-
118
- ### OpenAI Chat Format
119
-
120
- Perfect for integration with OpenAI API:
121
-
122
- ```ruby
123
- require 'poml'
124
- require 'json'
125
-
126
- markup = <<~POML
127
- <poml>
128
- <role>Helpful assistant specialized in Ruby programming</role>
129
- <task>Help debug Ruby code and suggest improvements</task>
130
- </poml>
131
- POML
132
-
133
- messages = Poml.process(markup: markup, format: 'openai_chat')
134
-
135
- # Use with OpenAI API
136
- require 'net/http'
137
- require 'uri'
138
-
139
- payload = {
140
- model: 'gpt-4',
141
- messages: messages,
142
- max_tokens: 1000
143
- }
144
-
145
- # Send to OpenAI API (example)
146
- puts JSON.pretty_generate(payload)
147
- ```
148
-
149
- ### LangChain Format
150
-
151
- Useful for LangChain-compatible applications:
152
-
153
- ```ruby
154
- require 'poml'
155
-
156
- markup = '<poml><role>SQL Expert</role><task>Help write database queries</task></poml>'
157
- result = Poml.process(markup: markup, format: 'langchain')
158
-
159
- puts result['messages'] # Array of message objects
160
- puts result['content'] # Raw content string
161
- ```
162
-
163
- ### Pydantic Format
164
-
165
- Returns a simplified structure with prompt and metadata:
166
-
167
- ```ruby
168
- require 'poml'
169
-
170
- markup = '<poml><role>Code Reviewer</role><task>Review Ruby code for best practices</task></poml>'
171
- result = Poml.process(markup: markup, format: 'pydantic')
172
-
173
- puts result['prompt'] # The formatted prompt
174
- puts result['variables'] # Template variables used
175
- puts result['chat_enabled'] # Whether chat mode is enabled
176
- ```
177
-
178
- ## Template Variables
179
-
180
- ### Basic Variable Substitution
181
-
182
- ```ruby
183
- require 'poml'
184
-
185
- markup = <<~POML
186
- <poml>
187
- <role>{{expert_type}} Expert</role>
188
- <task>{{main_task}}</task>
189
- <hint>Focus on {{focus_area}} and provide {{output_type}}</hint>
190
- </poml>
191
- POML
192
-
193
- context = {
194
- 'expert_type' => 'Machine Learning',
195
- 'main_task' => 'Analyze model performance',
196
- 'focus_area' => 'accuracy metrics',
197
- 'output_type' => 'actionable recommendations'
198
- }
199
-
200
- result = Poml.process(markup: markup, context: context)
201
- puts result['content']
202
- ```
203
-
204
- ### Dynamic Content with Variables
205
-
206
- ```ruby
207
- require 'poml'
208
-
209
- def create_code_review_prompt(language, complexity, focus_areas)
210
- markup = <<~POML
211
- <poml>
212
- <role>Senior {{language}} Developer</role>
213
- <task>Review the following {{language}} code</task>
214
-
215
- <p>Code complexity level: {{complexity}}</p>
216
-
217
- <p>Please focus on:</p>
218
- <list>
219
- {{#focus_areas}}
220
- <item>{{.}}</item>
221
- {{/focus_areas}}
222
- </list>
223
-
224
- <hint>Provide specific suggestions for improvement</hint>
225
- </poml>
226
- POML
227
-
228
- context = {
229
- 'language' => language,
230
- 'complexity' => complexity,
231
- 'focus_areas' => focus_areas
232
- }
233
-
234
- Poml.process(markup: markup, context: context, format: 'raw')
235
- end
236
-
237
- # Usage
238
- prompt = create_code_review_prompt(
239
- 'Ruby',
240
- 'Intermediate',
241
- ['Performance', 'Readability', 'Security', 'Testing']
242
- )
243
-
244
- puts prompt
245
- ```
246
-
247
- ### Conditional Content
248
-
249
- ```ruby
250
- require 'poml'
251
-
252
- markup = <<~POML
253
- <poml>
254
- <role>{{role_type}}</role>
255
- <task>{{task_description}}</task>
256
-
257
- {{#include_examples}}
258
- <p>Please include practical examples in your response.</p>
259
- {{/include_examples}}
260
-
261
- {{#urgency_level}}
262
- <hint>This is {{urgency_level}} priority - respond accordingly</hint>
263
- {{/urgency_level}}
264
- </poml>
265
- POML
266
-
267
- # High priority with examples
268
- context = {
269
- 'role_type' => 'Emergency Response Coordinator',
270
- 'task_description' => 'Coordinate disaster response',
271
- 'include_examples' => true,
272
- 'urgency_level' => 'HIGH'
273
- }
274
-
275
- result = Poml.process(markup: markup, context: context)
276
- puts result['content']
277
- ```
278
-
279
- ## Stylesheets
280
-
281
- ### Custom Component Styling
282
-
283
- ```ruby
284
- require 'poml'
285
-
286
- markup = <<~POML
287
- <poml>
288
- <role>API Documentation Writer</role>
289
- <task>Document REST endpoints</task>
290
-
291
- <cp caption="Authentication">
292
- <p>All requests require authentication via API key.</p>
293
- </cp>
294
-
295
- <cp caption="Rate Limiting">
296
- <p>API calls are limited to 1000 requests per hour.</p>
297
- </cp>
298
- </poml>
299
- POML
300
-
301
- stylesheet = {
302
- 'role' => {
303
- 'captionStyle' => 'bold',
304
- 'caption' => 'System Role'
305
- },
306
- 'cp' => {
307
- 'captionStyle' => 'header'
308
- }
309
- }
310
-
311
- result = Poml.process(markup: markup, stylesheet: stylesheet)
312
- puts result['content']
313
- ```
314
-
315
- ### Dynamic Styling
316
-
317
- ```ruby
318
- require 'poml'
319
-
320
- def create_styled_prompt(style_type = 'professional')
321
- markup = <<~POML
322
- <poml>
323
- <role>Technical Consultant</role>
324
- <task>Provide technical recommendations</task>
325
-
326
- <cp caption="Analysis">
327
- <p>Detailed technical analysis follows.</p>
328
- </cp>
329
-
330
- <cp caption="Recommendations">
331
- <p>Actionable recommendations based on analysis.</p>
332
- </cp>
333
- </poml>
334
- POML
335
-
336
- stylesheets = {
337
- 'professional' => {
338
- 'cp' => { 'captionStyle' => 'header' },
339
- 'role' => { 'captionStyle' => 'bold' }
340
- },
341
- 'casual' => {
342
- 'cp' => { 'captionStyle' => 'plain' },
343
- 'role' => { 'captionStyle' => 'plain' }
344
- },
345
- 'minimal' => {
346
- 'cp' => { 'captionStyle' => 'hidden' },
347
- 'role' => { 'captionStyle' => 'hidden' }
348
- }
349
- }
350
-
351
- Poml.process(
352
- markup: markup,
353
- stylesheet: stylesheets[style_type],
354
- format: 'raw'
355
- )
356
- end
357
-
358
- # Different styles
359
- puts "=== PROFESSIONAL STYLE ==="
360
- puts create_styled_prompt('professional')
361
-
362
- puts "\n=== CASUAL STYLE ==="
363
- puts create_styled_prompt('casual')
364
-
365
- puts "\n=== MINIMAL STYLE ==="
366
- puts create_styled_prompt('minimal')
367
- ```
368
-
369
- ## Working with Files
370
-
371
- ### Loading POML from Files
372
-
373
- ```ruby
374
- require 'poml'
375
-
376
- # Assuming you have a file 'prompts/code_review.poml'
377
- result = Poml.process(markup: 'prompts/code_review.poml')
378
- puts result['content']
379
- ```
380
-
381
- ### Saving Output to Files
382
-
383
- ```ruby
384
- require 'poml'
385
-
386
- markup = <<~POML
387
- <poml>
388
- <role>Report Generator</role>
389
- <task>Generate monthly performance report</task>
390
- </poml>
391
- POML
392
-
393
- # Save raw output
394
- Poml.process(
395
- markup: markup,
396
- format: 'raw',
397
- output_file: 'reports/prompt.txt'
398
- )
399
-
400
- # Save JSON output
401
- result = Poml.process(markup: markup, format: 'openai_chat')
402
- File.write('reports/messages.json', JSON.pretty_generate(result))
403
- ```
404
-
405
- ### Working with Context Files
406
-
407
- ```ruby
408
- require 'poml'
409
- require 'json'
410
-
411
- # Create context file
412
- context_data = {
413
- 'project_name' => 'E-commerce Platform',
414
- 'team_size' => 5,
415
- 'deadline' => '2024-12-31',
416
- 'technologies' => ['Ruby on Rails', 'PostgreSQL', 'Redis']
417
- }
418
-
419
- File.write('context.json', JSON.pretty_generate(context_data))
420
-
421
- # Use context from file
422
- markup = <<~POML
423
- <poml>
424
- <role>Project Manager</role>
425
- <task>Create project plan for {{project_name}}</task>
426
-
427
- <p>Team size: {{team_size}} developers</p>
428
- <p>Deadline: {{deadline}}</p>
429
-
430
- <p>Technology stack:</p>
431
- <list>
432
- {{#technologies}}
433
- <item>{{.}}</item>
434
- {{/technologies}}
435
- </list>
436
- </poml>
437
- POML
438
-
439
- # Load context from file
440
- context = JSON.parse(File.read('context.json'))
441
- result = Poml.process(markup: markup, context: context)
442
- puts result['content']
443
- ```
444
-
445
- ## Advanced Components
446
-
447
- ### Examples and Input/Output Patterns
448
-
449
- ```ruby
450
- require 'poml'
451
-
452
- markup = <<~POML
453
- <poml>
454
- <role>Code Generator</role>
455
- <task>Generate Ruby code based on examples</task>
456
-
457
- <example>
458
- <input>Create a User class with name and email</input>
459
- <output>
460
- class User
461
- attr_accessor :name, :email
462
-
463
- def initialize(name, email)
464
- @name = name
465
- @email = email
466
- end
467
- end
468
- </output>
469
- </example>
470
-
471
- <example>
472
- <input>Add validation to User class</input>
473
- <output>
474
- class User
475
- attr_accessor :name, :email
476
-
477
- def initialize(name, email)
478
- @name = name
479
- @email = email
480
- validate!
481
- end
482
-
483
- private
484
-
485
- def validate!
486
- raise ArgumentError, "Name cannot be empty" if name.nil? || name.strip.empty?
487
- raise ArgumentError, "Invalid email format" unless email.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
488
- end
489
- end
490
- </output>
491
- </example>
492
- </poml>
493
- POML
494
-
495
- result = Poml.process(markup: markup)
496
- puts result['content']
497
- ```
498
-
499
- ### Including External Documents
500
-
501
- ```ruby
502
- require 'poml'
503
-
504
- # Assuming you have external files
505
- markup = <<~POML
506
- <poml>
507
- <role>Code Reviewer</role>
508
- <task>Review the following code and suggest improvements</task>
509
-
510
- <Document src="examples/user_model.rb">
511
- </Document>
512
-
513
- <p>Please focus on:</p>
514
- <list>
515
- <item>Code organization</item>
516
- <item>Error handling</item>
517
- <item>Performance optimizations</item>
518
- </list>
519
- </poml>
520
- POML
521
-
522
- result = Poml.process(markup: markup)
523
- puts result['content']
524
- ```
525
-
526
- ### XML Syntax Mode
527
-
528
- ```ruby
529
- require 'poml'
530
-
531
- markup = <<~POML
532
- <poml syntax="xml">
533
- <role>System Architect</role>
534
- <cp caption="Requirements Analysis">
535
- <list>
536
- <item>Scalability requirements</item>
537
- <item>Performance benchmarks</item>
538
- <item>Security considerations</item>
539
- </list>
540
- </cp>
541
-
542
- <cp caption="Technical Recommendations">
543
- <p>Based on the analysis, recommend appropriate architecture.</p>
544
- </cp>
545
- </poml>
546
- POML
547
-
548
- result = Poml.process(markup: markup, format: 'raw')
549
- puts result
550
- ```
551
-
552
- ## Error Handling
553
-
554
- ### Basic Error Handling
555
-
556
- ```ruby
557
- require 'poml'
558
-
559
- begin
560
- # Invalid markup
561
- result = Poml.process(markup: '<poml><invalid_tag>Content</invalid_tag></poml>')
562
- rescue Poml::Error => e
563
- puts "POML Error: #{e.message}"
564
- rescue StandardError => e
565
- puts "General Error: #{e.message}"
566
- end
567
- ```
568
-
569
- ### Validating Markup Before Processing
570
-
571
- ```ruby
572
- require 'poml'
573
-
574
- def safe_process_poml(markup, options = {})
575
- # Basic validation
576
- return { error: "Empty markup" } if markup.nil? || markup.strip.empty?
577
- return { error: "Markup must contain <poml> tags" } unless markup.include?('<poml>')
578
-
579
- begin
580
- result = Poml.process(markup: markup, **options)
581
- { success: true, result: result }
582
- rescue Poml::Error => e
583
- { error: "POML processing error: #{e.message}" }
584
- rescue StandardError => e
585
- { error: "Unexpected error: #{e.message}" }
586
- end
587
- end
588
-
589
- # Usage
590
- markup = '<poml><role>Assistant</role><task>Help users</task></poml>'
591
- response = safe_process_poml(markup, format: 'raw')
592
-
593
- if response[:success]
594
- puts "Success: #{response[:result]}"
595
- else
596
- puts "Error: #{response[:error]}"
597
- end
598
- ```
599
-
600
- ## Integration Examples
601
-
602
- ### Rails Application Integration
603
-
604
- ```ruby
605
- # app/services/prompt_service.rb
606
- class PromptService
607
- include Rails.application.routes.url_helpers
608
-
609
- def self.generate_ai_prompt(template_name, context = {})
610
- template_path = Rails.root.join('app', 'prompts', "#{template_name}.poml")
611
-
612
- unless File.exist?(template_path)
613
- raise ArgumentError, "Template #{template_name} not found"
614
- end
615
-
616
- markup = File.read(template_path)
617
-
618
- Poml.process(
619
- markup: markup,
620
- context: context,
621
- format: 'openai_chat'
622
- )
623
- end
624
-
625
- def self.create_support_prompt(user, issue_type, description)
626
- markup = <<~POML
627
- <poml>
628
- <role>Customer Support Specialist</role>
629
- <task>Help resolve {{issue_type}} for user {{user_name}}</task>
630
-
631
- <p>User Information:</p>
632
- <list>
633
- <item>Name: {{user_name}}</item>
634
- <item>Email: {{user_email}}</item>
635
- <item>Account Type: {{account_type}}</item>
636
- </list>
637
-
638
- <p>Issue Description:</p>
639
- <p>{{description}}</p>
640
-
641
- <hint>Be empathetic and provide step-by-step solutions</hint>
642
- </poml>
643
- POML
644
-
645
- context = {
646
- 'user_name' => user.name,
647
- 'user_email' => user.email,
648
- 'account_type' => user.account_type,
649
- 'issue_type' => issue_type,
650
- 'description' => description
651
- }
652
-
653
- Poml.process(markup: markup, context: context, format: 'raw')
654
- end
655
- end
656
-
657
- # Usage in controller
658
- class SupportController < ApplicationController
659
- def generate_response
660
- prompt = PromptService.create_support_prompt(
661
- current_user,
662
- params[:issue_type],
663
- params[:description]
664
- )
665
-
666
- # Send to AI service
667
- ai_response = AIService.generate_response(prompt)
668
-
669
- render json: { response: ai_response }
670
- end
671
- end
672
- ```
673
-
674
- ### Background Job Integration
675
-
676
- ```ruby
677
- # app/jobs/content_generation_job.rb
678
- class ContentGenerationJob < ApplicationJob
679
- queue_as :default
680
-
681
- def perform(content_request_id)
682
- content_request = ContentRequest.find(content_request_id)
683
-
684
- begin
685
- prompt = generate_prompt(content_request)
686
-
687
- # Process with AI service
688
- generated_content = AIService.generate_content(prompt)
689
-
690
- content_request.update!(
691
- content: generated_content,
692
- status: 'completed'
693
- )
694
-
695
- rescue StandardError => e
696
- content_request.update!(
697
- status: 'failed',
698
- error_message: e.message
699
- )
700
-
701
- Rails.logger.error "Content generation failed: #{e.message}"
702
- end
703
- end
704
-
705
- private
706
-
707
- def generate_prompt(content_request)
708
- markup = <<~POML
709
- <poml>
710
- <role>{{content_type}} Writer</role>
711
- <task>Create {{content_type}} about {{topic}}</task>
712
-
713
- <p>Target audience: {{audience}}</p>
714
- <p>Tone: {{tone}}</p>
715
- <p>Length: {{length}} words</p>
716
-
717
- {{#keywords}}
718
- <p>Include these keywords: {{keywords}}</p>
719
- {{/keywords}}
720
-
721
- <hint>Make it engaging and informative</hint>
722
- </poml>
723
- POML
724
-
725
- context = {
726
- 'content_type' => content_request.content_type,
727
- 'topic' => content_request.topic,
728
- 'audience' => content_request.target_audience,
729
- 'tone' => content_request.tone,
730
- 'length' => content_request.word_count,
731
- 'keywords' => content_request.keywords
732
- }
733
-
734
- Poml.process(markup: markup, context: context, format: 'raw')
735
- end
736
- end
737
- ```
738
-
739
- ### Sinatra Application Example
740
-
741
- ```ruby
742
- require 'sinatra'
743
- require 'poml'
744
- require 'json'
745
-
746
- # Simple prompt API
747
- post '/api/prompts/generate' do
748
- content_type :json
749
-
750
- begin
751
- request_data = JSON.parse(request.body.read)
752
-
753
- markup = request_data['markup']
754
- context = request_data['context'] || {}
755
- format = request_data['format'] || 'dict'
756
-
757
- result = Poml.process(
758
- markup: markup,
759
- context: context,
760
- format: format
761
- )
762
-
763
- { success: true, result: result }.to_json
764
-
765
- rescue JSON::ParserError
766
- status 400
767
- { error: 'Invalid JSON' }.to_json
768
- rescue Poml::Error => e
769
- status 422
770
- { error: "POML Error: #{e.message}" }.to_json
771
- rescue StandardError => e
772
- status 500
773
- { error: "Server Error: #{e.message}" }.to_json
774
- end
775
- end
776
-
777
- # Template-based prompt generation
778
- get '/api/prompts/templates/:name' do
779
- content_type :json
780
-
781
- template_path = "templates/#{params[:name]}.poml"
782
-
783
- unless File.exist?(template_path)
784
- status 404
785
- return { error: 'Template not found' }.to_json
786
- end
787
-
788
- begin
789
- markup = File.read(template_path)
790
- context = params[:context] ? JSON.parse(params[:context]) : {}
791
-
792
- result = Poml.process(
793
- markup: markup,
794
- context: context,
795
- format: params[:format] || 'dict'
796
- )
797
-
798
- { success: true, result: result }.to_json
799
-
800
- rescue StandardError => e
801
- status 500
802
- { error: e.message }.to_json
803
- end
804
- end
805
- ```
806
-
807
- ## Best Practices
808
-
809
- ### 1. Template Organization
810
-
811
- ```ruby
812
- # Good: Organize templates in modules
813
- module PromptTemplates
814
- module CodeReview
815
- BASIC_REVIEW = <<~POML
816
- <poml>
817
- <role>Senior {{language}} Developer</role>
818
- <task>Review the following code for best practices</task>
819
- <hint>Focus on readability, performance, and maintainability</hint>
820
- </poml>
821
- POML
822
-
823
- SECURITY_REVIEW = <<~POML
824
- <poml>
825
- <role>Security Expert</role>
826
- <task>Review code for security vulnerabilities</task>
827
- <hint>Look for common security issues like injection attacks, XSS, etc.</hint>
828
- </poml>
829
- POML
830
- end
831
-
832
- module Documentation
833
- API_DOCS = <<~POML
834
- <poml>
835
- <role>Technical Writer</role>
836
- <task>Document the API endpoint</task>
837
- <hint>Include examples, parameters, and error responses</hint>
838
- </poml>
839
- POML
840
- end
841
- end
842
-
843
- # Usage
844
- def generate_code_review_prompt(language, code)
845
- context = { 'language' => language, 'code' => code }
846
- Poml.process(markup: PromptTemplates::CodeReview::BASIC_REVIEW, context: context)
847
- end
848
- ```
849
-
850
- ### 2. Context Validation
851
-
852
- ```ruby
853
- class PromptGenerator
854
- REQUIRED_FIELDS = {
855
- 'user_support' => %w[user_name issue_type description],
856
- 'code_review' => %w[language code_snippet],
857
- 'content_generation' => %w[topic audience tone]
858
- }.freeze
859
-
860
- def self.generate(template_type, context)
861
- validate_context!(template_type, context)
862
-
863
- template = load_template(template_type)
864
- Poml.process(markup: template, context: context)
865
- end
866
-
867
- private
868
-
869
- def self.validate_context!(template_type, context)
870
- required = REQUIRED_FIELDS[template_type]
871
- return unless required
872
-
873
- missing = required - context.keys
874
- raise ArgumentError, "Missing required fields: #{missing.join(', ')}" if missing.any?
875
- end
876
-
877
- def self.load_template(template_type)
878
- template_path = "templates/#{template_type}.poml"
879
- raise ArgumentError, "Template not found: #{template_type}" unless File.exist?(template_path)
880
-
881
- File.read(template_path)
882
- end
883
- end
884
- ```
885
-
886
- ### 3. Caching Strategies
887
-
888
- ```ruby
889
- require 'digest'
890
-
891
- class CachedPromptProcessor
892
- def initialize(cache_store = {})
893
- @cache = cache_store
894
- end
895
-
896
- def process(markup, context = {}, format = 'dict')
897
- cache_key = generate_cache_key(markup, context, format)
898
-
899
- @cache[cache_key] ||= Poml.process(
900
- markup: markup,
901
- context: context,
902
- format: format
903
- )
904
- end
905
-
906
- private
907
-
908
- def generate_cache_key(markup, context, format)
909
- content = "#{markup}#{context.to_json}#{format}"
910
- Digest::SHA256.hexdigest(content)
911
- end
912
- end
913
-
914
- # Usage
915
- processor = CachedPromptProcessor.new
916
- result1 = processor.process(markup, context) # Processes
917
- result2 = processor.process(markup, context) # Returns cached result
918
- ```
919
-
920
- ### 4. Environment-Specific Configuration
921
-
922
- ```ruby
923
- class PomlConfig
924
- def self.default_options
925
- case Rails.env
926
- when 'development'
927
- { chat: true, format: 'raw' }
928
- when 'test'
929
- { chat: false, format: 'dict' }
930
- when 'production'
931
- { chat: true, format: 'openai_chat' }
932
- else
933
- { chat: true, format: 'dict' }
934
- end
935
- end
936
-
937
- def self.process_with_defaults(markup, options = {})
938
- final_options = default_options.merge(options)
939
- Poml.process(markup: markup, **final_options)
940
- end
941
- end
942
-
943
- # Usage
944
- result = PomlConfig.process_with_defaults(markup, context: context)
945
- ```
946
-
947
- ### 5. Error Logging and Monitoring
948
-
949
- ```ruby
950
- class MonitoredPromptProcessor
951
- def self.process(markup, options = {})
952
- start_time = Time.current
953
-
954
- begin
955
- result = Poml.process(markup: markup, **options)
956
-
957
- log_success(markup, options, Time.current - start_time)
958
- result
959
-
960
- rescue Poml::Error => e
961
- log_poml_error(markup, options, e)
962
- raise
963
- rescue StandardError => e
964
- log_system_error(markup, options, e)
965
- raise
966
- end
967
- end
968
-
969
- private
970
-
971
- def self.log_success(markup, options, duration)
972
- Rails.logger.info "POML processed successfully in #{duration.round(3)}s"
973
- end
974
-
975
- def self.log_poml_error(markup, options, error)
976
- Rails.logger.error "POML Error: #{error.message}"
977
- Rails.logger.error "Markup: #{markup.truncate(200)}"
978
- end
979
-
980
- def self.log_system_error(markup, options, error)
981
- Rails.logger.error "System Error during POML processing: #{error.message}"
982
- Rails.logger.error error.backtrace.join("\n")
983
- end
984
- end
985
- ```
986
-
987
- This comprehensive tutorial covers the main use cases and patterns for using the POML Ruby gem. Each example is practical and can be adapted to your specific needs.