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.
- 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/lib/poml/components/base.rb +146 -4
- 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 +134 -5
- data/lib/poml/components/output_schema.rb +19 -1
- 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 +339 -10
- data/lib/poml/components/tools.rb +14 -0
- data/lib/poml/components/utilities.rb +34 -18
- data/lib/poml/components.rb +19 -0
- data/lib/poml/context.rb +19 -4
- data/lib/poml/parser.rb +88 -63
- 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 +31 -4
- data/TUTORIAL.md +0 -987
@@ -0,0 +1,688 @@
|
|
1
|
+
# Output Formats
|
2
|
+
|
3
|
+
POML supports multiple output formats to integrate seamlessly with different AI services, frameworks, and applications.
|
4
|
+
|
5
|
+
## Two Types of Format Control
|
6
|
+
|
7
|
+
POML provides **two distinct levels** of format control:
|
8
|
+
|
9
|
+
### 1. Output Structure Formats
|
10
|
+
|
11
|
+
Control the **overall structure** of the result (API format):
|
12
|
+
|
13
|
+
- `format: 'raw'` - Plain text with role boundaries
|
14
|
+
- `format: 'dict'` - Ruby Hash with metadata (default)
|
15
|
+
- `format: 'openai_chat'` - OpenAI message arrays
|
16
|
+
- `format: 'openaiResponse'` - Standardized AI response
|
17
|
+
- `format: 'langchain'` - LangChain compatible
|
18
|
+
- `format: 'pydantic'` - Python interoperability
|
19
|
+
|
20
|
+
### 2. Content Rendering Formats
|
21
|
+
|
22
|
+
Control how **components render** within the content using `<output format="..."/>`:
|
23
|
+
|
24
|
+
- `format="markdown"` - Markdown syntax (default): `# Header`, `**bold**`
|
25
|
+
- `format="html"` - HTML tags: `<h1>Header</h1>`, `<b>bold</b>`
|
26
|
+
- `format="text"` - Plain text without markup
|
27
|
+
- `format="json"` - JSON representation of content
|
28
|
+
- `format="xml"` - XML-structured content
|
29
|
+
|
30
|
+
**Example combining both:**
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
markup = <<~POML
|
34
|
+
<poml>
|
35
|
+
<role>Writer</role>
|
36
|
+
<h1>Article Title</h1>
|
37
|
+
<p>Content with <b>emphasis</b></p>
|
38
|
+
<output format="html"/> <!-- Content format -->
|
39
|
+
</poml>
|
40
|
+
POML
|
41
|
+
|
42
|
+
# Structure format
|
43
|
+
result = Poml.process(markup: markup, format: 'dict')
|
44
|
+
puts result['output'] # HTML: <h1>Article Title</h1><p>Content with <b>emphasis</b></p>
|
45
|
+
```
|
46
|
+
|
47
|
+
## Available Formats
|
48
|
+
|
49
|
+
POML provides six main output formats:
|
50
|
+
|
51
|
+
- **raw** - Plain text with role boundaries
|
52
|
+
- **dict** - Ruby Hash with structured metadata
|
53
|
+
- **openai_chat** - OpenAI API compatible message arrays
|
54
|
+
- **openaiResponse** - Standardized AI response structure
|
55
|
+
- **langchain** - LangChain compatible format
|
56
|
+
- **pydantic** - Python interoperability with strict schemas
|
57
|
+
|
58
|
+
## Raw Format
|
59
|
+
|
60
|
+
The raw format outputs plain text with role boundaries, perfect for direct use with AI APIs:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
require 'poml'
|
64
|
+
|
65
|
+
markup = <<~POML
|
66
|
+
<poml>
|
67
|
+
<role>Ruby Expert</role>
|
68
|
+
<task>Review code for best practices</task>
|
69
|
+
<hint>Focus on performance and readability</hint>
|
70
|
+
</poml>
|
71
|
+
POML
|
72
|
+
|
73
|
+
result = Poml.process(markup: markup, format: 'raw')
|
74
|
+
puts result
|
75
|
+
```
|
76
|
+
|
77
|
+
Output:
|
78
|
+
|
79
|
+
```
|
80
|
+
===== system =====
|
81
|
+
|
82
|
+
# Role
|
83
|
+
|
84
|
+
Ruby Expert
|
85
|
+
|
86
|
+
# Task
|
87
|
+
|
88
|
+
Review code for best practices
|
89
|
+
|
90
|
+
# Hint
|
91
|
+
|
92
|
+
Focus on performance and readability
|
93
|
+
```
|
94
|
+
|
95
|
+
### Raw Format with Chat Components
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
markup = <<~POML
|
99
|
+
<poml>
|
100
|
+
<system>You are a helpful programming assistant.</system>
|
101
|
+
<human>How do I optimize this Ruby code?</human>
|
102
|
+
</poml>
|
103
|
+
POML
|
104
|
+
|
105
|
+
result = Poml.process(markup: markup, format: 'raw')
|
106
|
+
```
|
107
|
+
|
108
|
+
Output:
|
109
|
+
|
110
|
+
```
|
111
|
+
===== system =====
|
112
|
+
|
113
|
+
You are a helpful programming assistant.
|
114
|
+
|
115
|
+
===== human =====
|
116
|
+
|
117
|
+
How do I optimize this Ruby code?
|
118
|
+
```
|
119
|
+
|
120
|
+
## Dictionary Format (Default)
|
121
|
+
|
122
|
+
The dictionary format returns a Ruby Hash with structured content and metadata:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
markup = <<~POML
|
126
|
+
<poml>
|
127
|
+
<role>Data Analyst</role>
|
128
|
+
<task>Analyze sales data</task>
|
129
|
+
<meta variables='{"quarter": "Q4", "year": 2024}' />
|
130
|
+
</poml>
|
131
|
+
POML
|
132
|
+
|
133
|
+
result = Poml.process(markup: markup, format: 'dict')
|
134
|
+
# or simply: result = Poml.process(markup: markup)
|
135
|
+
|
136
|
+
puts result.class # Hash
|
137
|
+
puts result['content'] # The formatted prompt
|
138
|
+
puts result['metadata'] # Additional information
|
139
|
+
```
|
140
|
+
|
141
|
+
Structure:
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
{
|
145
|
+
'content' => "The formatted prompt text",
|
146
|
+
'metadata' => {
|
147
|
+
'chat' => true,
|
148
|
+
'variables' => {'quarter' => 'Q4', 'year' => 2024},
|
149
|
+
'schemas' => [],
|
150
|
+
'tools' => [],
|
151
|
+
'custom_metadata' => {}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
```
|
155
|
+
|
156
|
+
## OpenAI Chat Format
|
157
|
+
|
158
|
+
Perfect for integration with OpenAI's Chat Completions API:
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
markup = <<~POML
|
162
|
+
<poml>
|
163
|
+
<system>You are a code review assistant specializing in Ruby.</system>
|
164
|
+
<human>Please review this method for potential improvements.</human>
|
165
|
+
</poml>
|
166
|
+
POML
|
167
|
+
|
168
|
+
messages = Poml.process(markup: markup, format: 'openai_chat')
|
169
|
+
puts JSON.pretty_generate(messages)
|
170
|
+
```
|
171
|
+
|
172
|
+
Output:
|
173
|
+
|
174
|
+
```json
|
175
|
+
[
|
176
|
+
{
|
177
|
+
"role": "system",
|
178
|
+
"content": "You are a code review assistant specializing in Ruby."
|
179
|
+
},
|
180
|
+
{
|
181
|
+
"role": "user",
|
182
|
+
"content": "Please review this method for potential improvements."
|
183
|
+
}
|
184
|
+
]
|
185
|
+
```
|
186
|
+
|
187
|
+
### With Template Variables
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
markup = <<~POML
|
191
|
+
<poml>
|
192
|
+
<system>You are a {{expertise}} expert.</system>
|
193
|
+
<human>Help me with {{task_type}}.</human>
|
194
|
+
</poml>
|
195
|
+
POML
|
196
|
+
|
197
|
+
context = {
|
198
|
+
'expertise' => 'Ruby on Rails',
|
199
|
+
'task_type' => 'performance optimization'
|
200
|
+
}
|
201
|
+
|
202
|
+
messages = Poml.process(markup: markup, context: context, format: 'openai_chat')
|
203
|
+
```
|
204
|
+
|
205
|
+
### Direct OpenAI Integration
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
require 'net/http'
|
209
|
+
require 'json'
|
210
|
+
|
211
|
+
def send_to_openai(poml_markup, context = {})
|
212
|
+
messages = Poml.process(
|
213
|
+
markup: poml_markup,
|
214
|
+
context: context,
|
215
|
+
format: 'openai_chat'
|
216
|
+
)
|
217
|
+
|
218
|
+
payload = {
|
219
|
+
model: 'gpt-4',
|
220
|
+
messages: messages,
|
221
|
+
max_tokens: 1000,
|
222
|
+
temperature: 0.7
|
223
|
+
}
|
224
|
+
|
225
|
+
uri = URI('https://api.openai.com/v1/chat/completions')
|
226
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
227
|
+
http.use_ssl = true
|
228
|
+
|
229
|
+
request = Net::HTTP::Post.new(uri)
|
230
|
+
request['Authorization'] = "Bearer #{ENV['OPENAI_API_KEY']}"
|
231
|
+
request['Content-Type'] = 'application/json'
|
232
|
+
request.body = JSON.generate(payload)
|
233
|
+
|
234
|
+
response = http.request(request)
|
235
|
+
JSON.parse(response.body)
|
236
|
+
end
|
237
|
+
|
238
|
+
# Usage
|
239
|
+
markup = <<~POML
|
240
|
+
<poml>
|
241
|
+
<system>You are a helpful assistant.</system>
|
242
|
+
<human>Explain recursion in programming.</human>
|
243
|
+
</poml>
|
244
|
+
POML
|
245
|
+
|
246
|
+
response = send_to_openai(markup)
|
247
|
+
puts response['choices'][0]['message']['content']
|
248
|
+
```
|
249
|
+
|
250
|
+
## OpenAI Response Format
|
251
|
+
|
252
|
+
Standardized AI response structure separate from conversational chat format:
|
253
|
+
|
254
|
+
```ruby
|
255
|
+
markup = <<~POML
|
256
|
+
<poml>
|
257
|
+
<role>Research Assistant</role>
|
258
|
+
<task>Summarize research findings</task>
|
259
|
+
|
260
|
+
<output-schema name="ResearchSummary">
|
261
|
+
{
|
262
|
+
"type": "object",
|
263
|
+
"properties": {
|
264
|
+
"key_findings": {"type": "array"},
|
265
|
+
"confidence": {"type": "number"}
|
266
|
+
}
|
267
|
+
}
|
268
|
+
</output-schema>
|
269
|
+
</poml>
|
270
|
+
POML
|
271
|
+
|
272
|
+
result = Poml.process(markup: markup, format: 'openaiResponse')
|
273
|
+
puts JSON.pretty_generate(result)
|
274
|
+
```
|
275
|
+
|
276
|
+
Output:
|
277
|
+
|
278
|
+
```json
|
279
|
+
{
|
280
|
+
"content": "Research Assistant\n\nSummarize research findings",
|
281
|
+
"type": "prompt",
|
282
|
+
"metadata": {
|
283
|
+
"variables": {},
|
284
|
+
"schemas": [
|
285
|
+
{
|
286
|
+
"name": "ResearchSummary",
|
287
|
+
"content": {"type": "object", "properties": {...}}
|
288
|
+
}
|
289
|
+
],
|
290
|
+
"tools": [],
|
291
|
+
"custom_metadata": {}
|
292
|
+
}
|
293
|
+
}
|
294
|
+
```
|
295
|
+
|
296
|
+
### With Tools and Schemas
|
297
|
+
|
298
|
+
```ruby
|
299
|
+
markup = <<~POML
|
300
|
+
<poml>
|
301
|
+
<role>Data Analyst</role>
|
302
|
+
<task>Analyze dataset and provide insights</task>
|
303
|
+
|
304
|
+
<tool-definition name="load_data">
|
305
|
+
{
|
306
|
+
"description": "Load dataset from file",
|
307
|
+
"parameters": {
|
308
|
+
"type": "object",
|
309
|
+
"properties": {
|
310
|
+
"file_path": {"type": "string"},
|
311
|
+
"format": {"type": "string"}
|
312
|
+
}
|
313
|
+
}
|
314
|
+
}
|
315
|
+
</tool-definition>
|
316
|
+
|
317
|
+
<output-schema name="Analysis">
|
318
|
+
{
|
319
|
+
"type": "object",
|
320
|
+
"properties": {
|
321
|
+
"insights": {"type": "array"},
|
322
|
+
"recommendations": {"type": "array"}
|
323
|
+
}
|
324
|
+
}
|
325
|
+
</output-schema>
|
326
|
+
</poml>
|
327
|
+
POML
|
328
|
+
|
329
|
+
result = Poml.process(markup: markup, format: 'openaiResponse')
|
330
|
+
```
|
331
|
+
|
332
|
+
## LangChain Format
|
333
|
+
|
334
|
+
Compatible with LangChain framework for Python/Node.js integration:
|
335
|
+
|
336
|
+
```ruby
|
337
|
+
markup = <<~POML
|
338
|
+
<poml>
|
339
|
+
<system>You are a helpful assistant.</system>
|
340
|
+
<human>Explain machine learning concepts.</human>
|
341
|
+
</poml>
|
342
|
+
POML
|
343
|
+
|
344
|
+
result = Poml.process(markup: markup, format: 'langchain')
|
345
|
+
puts JSON.pretty_generate(result)
|
346
|
+
```
|
347
|
+
|
348
|
+
Output:
|
349
|
+
|
350
|
+
```json
|
351
|
+
{
|
352
|
+
"messages": [
|
353
|
+
{
|
354
|
+
"role": "system",
|
355
|
+
"content": "You are a helpful assistant."
|
356
|
+
},
|
357
|
+
{
|
358
|
+
"role": "human",
|
359
|
+
"content": "Explain machine learning concepts."
|
360
|
+
}
|
361
|
+
],
|
362
|
+
"content": "===== system =====\n\nYou are a helpful assistant.\n\n===== human =====\n\nExplain machine learning concepts."
|
363
|
+
}
|
364
|
+
```
|
365
|
+
|
366
|
+
### LangChain Integration Example
|
367
|
+
|
368
|
+
```python
|
369
|
+
# Python example using the output
|
370
|
+
import json
|
371
|
+
from langchain.schema import SystemMessage, HumanMessage
|
372
|
+
|
373
|
+
def process_poml_for_langchain(poml_output):
|
374
|
+
messages = []
|
375
|
+
for msg in poml_output['messages']:
|
376
|
+
if msg['role'] == 'system':
|
377
|
+
messages.append(SystemMessage(content=msg['content']))
|
378
|
+
elif msg['role'] == 'human':
|
379
|
+
messages.append(HumanMessage(content=msg['content']))
|
380
|
+
return messages
|
381
|
+
```
|
382
|
+
|
383
|
+
## Pydantic Format
|
384
|
+
|
385
|
+
Enhanced Python interoperability with strict JSON schemas:
|
386
|
+
|
387
|
+
```ruby
|
388
|
+
markup = <<~POML
|
389
|
+
<poml>
|
390
|
+
<role>API Designer</role>
|
391
|
+
<task>Design REST API endpoints</task>
|
392
|
+
|
393
|
+
<meta title="API Design Session" author="Senior Developer" />
|
394
|
+
|
395
|
+
<output-schema name="APIDesign">
|
396
|
+
{
|
397
|
+
"type": "object",
|
398
|
+
"properties": {
|
399
|
+
"endpoints": {"type": "array"},
|
400
|
+
"schemas": {"type": "object"}
|
401
|
+
}
|
402
|
+
}
|
403
|
+
</output-schema>
|
404
|
+
</poml>
|
405
|
+
POML
|
406
|
+
|
407
|
+
result = Poml.process(markup: markup, format: 'pydantic')
|
408
|
+
puts JSON.pretty_generate(result)
|
409
|
+
```
|
410
|
+
|
411
|
+
Output:
|
412
|
+
|
413
|
+
```json
|
414
|
+
{
|
415
|
+
"content": "API Designer\n\nDesign REST API endpoints",
|
416
|
+
"variables": {},
|
417
|
+
"chat_enabled": true,
|
418
|
+
"schemas": [
|
419
|
+
{
|
420
|
+
"name": "APIDesign",
|
421
|
+
"schema": {"type": "object", "properties": {...}}
|
422
|
+
}
|
423
|
+
],
|
424
|
+
"custom_metadata": {
|
425
|
+
"title": "API Design Session",
|
426
|
+
"author": "Senior Developer"
|
427
|
+
},
|
428
|
+
"metadata": {
|
429
|
+
"format": "pydantic",
|
430
|
+
"python_compatibility": true,
|
431
|
+
"strict_schemas": true
|
432
|
+
}
|
433
|
+
}
|
434
|
+
```
|
435
|
+
|
436
|
+
### Python Integration
|
437
|
+
|
438
|
+
```python
|
439
|
+
# Python example using pydantic output
|
440
|
+
from pydantic import BaseModel, create_model
|
441
|
+
import json
|
442
|
+
|
443
|
+
def create_pydantic_model(poml_output):
|
444
|
+
"""Create Pydantic model from POML schema output"""
|
445
|
+
schemas = poml_output.get('schemas', [])
|
446
|
+
|
447
|
+
if schemas:
|
448
|
+
schema = schemas[0]['schema']
|
449
|
+
model_name = schemas[0]['name']
|
450
|
+
|
451
|
+
# Create Pydantic model from JSON schema
|
452
|
+
return create_model(model_name, **schema['properties'])
|
453
|
+
|
454
|
+
return None
|
455
|
+
```
|
456
|
+
|
457
|
+
## Format Comparison
|
458
|
+
|
459
|
+
### When to Use Each Format
|
460
|
+
|
461
|
+
| Format | Best For | Output Type | Metadata |
|
462
|
+
|--------|----------|-------------|----------|
|
463
|
+
| `raw` | Direct AI API calls, Claude, custom models | String | None |
|
464
|
+
| `dict` | Ruby applications, general processing | Hash | Full |
|
465
|
+
| `openai_chat` | OpenAI Chat Completions API | Array | None |
|
466
|
+
| `openaiResponse` | Structured AI responses with metadata | Hash | Full |
|
467
|
+
| `langchain` | LangChain framework integration | Hash | Partial |
|
468
|
+
| `pydantic` | Python applications, strict schemas | Hash | Enhanced |
|
469
|
+
|
470
|
+
### Performance Characteristics
|
471
|
+
|
472
|
+
```ruby
|
473
|
+
require 'benchmark'
|
474
|
+
|
475
|
+
markup = <<~POML
|
476
|
+
<poml>
|
477
|
+
<role>Performance Tester</role>
|
478
|
+
<task>Test processing speed</task>
|
479
|
+
</poml>
|
480
|
+
POML
|
481
|
+
|
482
|
+
Benchmark.bm(15) do |x|
|
483
|
+
x.report('raw:') { 1000.times { Poml.process(markup: markup, format: 'raw') } }
|
484
|
+
x.report('dict:') { 1000.times { Poml.process(markup: markup, format: 'dict') } }
|
485
|
+
x.report('openai_chat:') { 1000.times { Poml.process(markup: markup, format: 'openai_chat') } }
|
486
|
+
x.report('openaiResponse:') { 1000.times { Poml.process(markup: markup, format: 'openaiResponse') } }
|
487
|
+
x.report('langchain:') { 1000.times { Poml.process(markup: markup, format: 'langchain') } }
|
488
|
+
x.report('pydantic:') { 1000.times { Poml.process(markup: markup, format: 'pydantic') } }
|
489
|
+
end
|
490
|
+
```
|
491
|
+
|
492
|
+
## Advanced Format Usage
|
493
|
+
|
494
|
+
### Dynamic Format Selection
|
495
|
+
|
496
|
+
```ruby
|
497
|
+
class PomlFormatter
|
498
|
+
FORMATS = {
|
499
|
+
openai: 'openai_chat',
|
500
|
+
claude: 'raw',
|
501
|
+
langchain: 'langchain',
|
502
|
+
python: 'pydantic',
|
503
|
+
default: 'dict'
|
504
|
+
}.freeze
|
505
|
+
|
506
|
+
def self.process_for_service(markup, service, context = {})
|
507
|
+
format = FORMATS[service] || FORMATS[:default]
|
508
|
+
Poml.process(markup: markup, context: context, format: format)
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
# Usage
|
513
|
+
markup = '<poml><role>Assistant</role><task>Help users</task></poml>'
|
514
|
+
|
515
|
+
openai_result = PomlFormatter.process_for_service(markup, :openai)
|
516
|
+
claude_result = PomlFormatter.process_for_service(markup, :claude)
|
517
|
+
python_result = PomlFormatter.process_for_service(markup, :python)
|
518
|
+
```
|
519
|
+
|
520
|
+
### Format Validation
|
521
|
+
|
522
|
+
```ruby
|
523
|
+
def validate_format_output(markup, format)
|
524
|
+
result = Poml.process(markup: markup, format: format)
|
525
|
+
|
526
|
+
case format
|
527
|
+
when 'openai_chat'
|
528
|
+
return result.is_a?(Array) && result.all? { |msg| msg['role'] && msg['content'] }
|
529
|
+
when 'dict', 'openaiResponse', 'langchain', 'pydantic'
|
530
|
+
return result.is_a?(Hash) && result['content']
|
531
|
+
when 'raw'
|
532
|
+
return result.is_a?(String)
|
533
|
+
else
|
534
|
+
false
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
# Test all formats
|
539
|
+
formats = ['raw', 'dict', 'openai_chat', 'openaiResponse', 'langchain', 'pydantic']
|
540
|
+
markup = '<poml><role>Tester</role><task>Test formats</task></poml>'
|
541
|
+
|
542
|
+
formats.each do |format|
|
543
|
+
valid = validate_format_output(markup, format)
|
544
|
+
puts "#{format}: #{valid ? '✅' : '❌'}"
|
545
|
+
end
|
546
|
+
```
|
547
|
+
|
548
|
+
### Content Consistency Testing
|
549
|
+
|
550
|
+
```ruby
|
551
|
+
def test_content_consistency(markup, context = {})
|
552
|
+
formats = ['raw', 'dict', 'openai_chat', 'openaiResponse', 'langchain', 'pydantic']
|
553
|
+
results = {}
|
554
|
+
|
555
|
+
formats.each do |format|
|
556
|
+
results[format] = Poml.process(markup: markup, context: context, format: format)
|
557
|
+
end
|
558
|
+
|
559
|
+
# Extract content from each format
|
560
|
+
contents = {
|
561
|
+
'raw' => results['raw'],
|
562
|
+
'dict' => results['dict']['content'],
|
563
|
+
'openai_chat' => results['openai_chat'].map { |msg| msg['content'] }.join("\n"),
|
564
|
+
'openaiResponse' => results['openaiResponse']['content'],
|
565
|
+
'langchain' => results['langchain']['content'],
|
566
|
+
'pydantic' => results['pydantic']['content']
|
567
|
+
}
|
568
|
+
|
569
|
+
# Check if core content is consistent
|
570
|
+
base_content = contents['dict'].gsub(/=+\s*\w+\s*=+/, '').strip
|
571
|
+
|
572
|
+
contents.each do |format, content|
|
573
|
+
processed_content = content.gsub(/=+\s*\w+\s*=+/, '').strip
|
574
|
+
consistent = processed_content.include?(base_content.split("\n").first)
|
575
|
+
puts "#{format}: #{consistent ? '✅' : '❌'} content consistency"
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
# Test consistency
|
580
|
+
markup = <<~POML
|
581
|
+
<poml>
|
582
|
+
<role>{{role_type}}</role>
|
583
|
+
<task>{{task_description}}</task>
|
584
|
+
</poml>
|
585
|
+
POML
|
586
|
+
|
587
|
+
context = { 'role_type' => 'Analyst', 'task_description' => 'Analyze data' }
|
588
|
+
test_content_consistency(markup, context)
|
589
|
+
```
|
590
|
+
|
591
|
+
## Integration Examples
|
592
|
+
|
593
|
+
### Multi-Service AI Client
|
594
|
+
|
595
|
+
```ruby
|
596
|
+
class MultiServiceAIClient
|
597
|
+
def initialize
|
598
|
+
@services = {
|
599
|
+
openai: { format: 'openai_chat', api_class: OpenAIClient },
|
600
|
+
claude: { format: 'raw', api_class: ClaudeClient },
|
601
|
+
langchain: { format: 'langchain', api_class: LangChainClient }
|
602
|
+
}
|
603
|
+
end
|
604
|
+
|
605
|
+
def process_prompt(service, markup, context = {})
|
606
|
+
config = @services[service]
|
607
|
+
return nil unless config
|
608
|
+
|
609
|
+
formatted_prompt = Poml.process(
|
610
|
+
markup: markup,
|
611
|
+
context: context,
|
612
|
+
format: config[:format]
|
613
|
+
)
|
614
|
+
|
615
|
+
config[:api_class].new.send_request(formatted_prompt)
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
# Usage
|
620
|
+
client = MultiServiceAIClient.new
|
621
|
+
|
622
|
+
markup = <<~POML
|
623
|
+
<poml>
|
624
|
+
<system>You are a helpful assistant.</system>
|
625
|
+
<human>Explain {{topic}} in simple terms.</human>
|
626
|
+
</poml>
|
627
|
+
POML
|
628
|
+
|
629
|
+
context = { 'topic' => 'quantum computing' }
|
630
|
+
|
631
|
+
openai_response = client.process_prompt(:openai, markup, context)
|
632
|
+
claude_response = client.process_prompt(:claude, markup, context)
|
633
|
+
```
|
634
|
+
|
635
|
+
### Format-Specific Optimization
|
636
|
+
|
637
|
+
```ruby
|
638
|
+
class OptimizedPomlProcessor
|
639
|
+
def self.process_optimized(markup, target_format, context = {})
|
640
|
+
case target_format
|
641
|
+
when 'openai_chat'
|
642
|
+
# Optimize for OpenAI - ensure proper chat structure
|
643
|
+
result = Poml.process(markup: markup, context: context, format: 'openai_chat')
|
644
|
+
# Add any OpenAI-specific optimizations
|
645
|
+
optimize_for_openai(result)
|
646
|
+
|
647
|
+
when 'raw'
|
648
|
+
# Optimize for raw text - clean formatting
|
649
|
+
result = Poml.process(markup: markup, context: context, format: 'raw')
|
650
|
+
result.gsub(/\n{3,}/, "\n\n") # Remove excessive newlines
|
651
|
+
|
652
|
+
when 'pydantic'
|
653
|
+
# Optimize for Python - ensure schema compatibility
|
654
|
+
result = Poml.process(markup: markup, context: context, format: 'pydantic')
|
655
|
+
ensure_python_compatibility(result)
|
656
|
+
|
657
|
+
else
|
658
|
+
Poml.process(markup: markup, context: context, format: target_format)
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
private
|
663
|
+
|
664
|
+
def self.optimize_for_openai(messages)
|
665
|
+
# Ensure messages don't exceed token limits, merge if needed
|
666
|
+
messages.map do |msg|
|
667
|
+
if msg['content'].length > 4000
|
668
|
+
msg['content'] = msg['content'][0..3000] + "... (truncated)"
|
669
|
+
end
|
670
|
+
msg
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
def self.ensure_python_compatibility(result)
|
675
|
+
# Ensure all schemas are valid JSON Schema Draft 7
|
676
|
+
result['schemas']&.each do |schema|
|
677
|
+
schema['schema']['$schema'] = 'http://json-schema.org/draft-07/schema#'
|
678
|
+
end
|
679
|
+
result
|
680
|
+
end
|
681
|
+
end
|
682
|
+
```
|
683
|
+
|
684
|
+
## Next Steps
|
685
|
+
|
686
|
+
- Learn about [Template Engine](template-engine.md) for dynamic content
|
687
|
+
- Explore [Schema Components](components/schema-components.md) for structured outputs
|
688
|
+
- Check [Integration Examples](integration/rails.md) for real-world usage
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Quick Start
|
2
|
+
|
3
|
+
Here's a very simple POML example. Please put it in a file named `example.poml`. Make sure it resides in the same directory as the `photosynthesis_diagram.png` image file.
|
4
|
+
|
5
|
+
```xml
|
6
|
+
<poml>
|
7
|
+
<role>You are a patient teacher explaining concepts to a 10-year-old.</role>
|
8
|
+
<task>Explain the concept of photosynthesis using the provided image as a reference.</task>
|
9
|
+
|
10
|
+
<img src="photosynthesis_diagram.png" alt="Diagram of photosynthesis" />
|
11
|
+
|
12
|
+
<output-format>
|
13
|
+
Keep the explanation simple, engaging, and under 100 words.
|
14
|
+
Start with "Hey there, future scientist!".
|
15
|
+
</output-format>
|
16
|
+
</poml>
|
17
|
+
```
|
18
|
+
|
19
|
+
This example defines a role and task for the LLM, includes an image for context, and specifies the desired output format. With the POML toolkit, the prompt can be easily rendered with a flexible format, and tested with a vision LLM.
|
20
|
+
|
21
|
+
## YouTube Video
|
22
|
+
|
23
|
+
We also recommend watching our [YouTube video](https://youtu.be/b9WDcFsKixo) for a quick introduction to POML and how to get started.
|
24
|
+
|
25
|
+
## Next Steps
|
26
|
+
|
27
|
+
- [Learn POML Syntax](basic-usage.md): Understand the structure and syntax of POML with Ruby examples.
|
28
|
+
- [Explore Components](components/index.md): Discover the available components and how to use them.
|
29
|
+
- [Ruby SDK Guide](../ruby/index.md): Learn how to use POML in your Ruby applications.
|
30
|
+
- [Template Engine](template-engine.md): Learn about variables, conditionals, and loops in POML.
|