poml 0.0.5 → 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.
- checksums.yaml +4 -4
- data/docs/tutorial/advanced/performance.md +695 -0
- data/docs/tutorial/advanced/tool-registration.md +776 -0
- data/docs/tutorial/basic-usage.md +351 -0
- data/docs/tutorial/components/chat-components.md +552 -0
- data/docs/tutorial/components/formatting.md +623 -0
- data/docs/tutorial/components/index.md +366 -0
- data/docs/tutorial/components/media-components.md +259 -0
- data/docs/tutorial/components/schema-components.md +668 -0
- data/docs/tutorial/index.md +184 -0
- data/docs/tutorial/output-formats.md +688 -0
- data/docs/tutorial/quickstart.md +30 -0
- data/docs/tutorial/template-engine.md +540 -0
- data/examples/303_new_component_syntax.poml +45 -0
- data/lib/poml/components/base.rb +150 -3
- data/lib/poml/components/content.rb +10 -3
- data/lib/poml/components/data.rb +539 -19
- data/lib/poml/components/examples.rb +235 -1
- data/lib/poml/components/formatting.rb +184 -18
- data/lib/poml/components/layout.rb +7 -2
- data/lib/poml/components/lists.rb +69 -35
- data/lib/poml/components/meta.rb +191 -6
- data/lib/poml/components/output_schema.rb +103 -0
- data/lib/poml/components/template.rb +72 -61
- data/lib/poml/components/text.rb +30 -1
- data/lib/poml/components/tool.rb +81 -0
- data/lib/poml/components/tool_definition.rb +427 -0
- data/lib/poml/components/tools.rb +14 -0
- data/lib/poml/components/utilities.rb +34 -18
- data/lib/poml/components.rb +29 -0
- data/lib/poml/context.rb +19 -4
- data/lib/poml/parser.rb +90 -64
- data/lib/poml/renderer.rb +191 -9
- data/lib/poml/template_engine.rb +138 -13
- data/lib/poml/version.rb +1 -1
- data/lib/poml.rb +16 -1
- data/readme.md +154 -27
- metadata +34 -4
- 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.
|