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.
Files changed (39) 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/examples/303_new_component_syntax.poml +45 -0
  15. data/lib/poml/components/base.rb +150 -3
  16. data/lib/poml/components/content.rb +10 -3
  17. data/lib/poml/components/data.rb +539 -19
  18. data/lib/poml/components/examples.rb +235 -1
  19. data/lib/poml/components/formatting.rb +184 -18
  20. data/lib/poml/components/layout.rb +7 -2
  21. data/lib/poml/components/lists.rb +69 -35
  22. data/lib/poml/components/meta.rb +191 -6
  23. data/lib/poml/components/output_schema.rb +103 -0
  24. data/lib/poml/components/template.rb +72 -61
  25. data/lib/poml/components/text.rb +30 -1
  26. data/lib/poml/components/tool.rb +81 -0
  27. data/lib/poml/components/tool_definition.rb +427 -0
  28. data/lib/poml/components/tools.rb +14 -0
  29. data/lib/poml/components/utilities.rb +34 -18
  30. data/lib/poml/components.rb +29 -0
  31. data/lib/poml/context.rb +19 -4
  32. data/lib/poml/parser.rb +90 -64
  33. data/lib/poml/renderer.rb +191 -9
  34. data/lib/poml/template_engine.rb +138 -13
  35. data/lib/poml/version.rb +1 -1
  36. data/lib/poml.rb +16 -1
  37. data/readme.md +154 -27
  38. metadata +34 -4
  39. data/TUTORIAL.md +0 -987
@@ -0,0 +1,668 @@
1
+ # Schema Components
2
+
3
+ POML provides powerful schema components for defining structured AI responses and tool definitions that work across different AI platforms.
4
+
5
+ ## Overview
6
+
7
+ Schema components allow you to define:
8
+
9
+ - **Output Schemas** - Structure for AI responses
10
+ - **Tool Definitions** - AI function calling capabilities
11
+ - **Metadata Integration** - Automatic inclusion in response metadata
12
+
13
+ ## Output Schema Component
14
+
15
+ The `<output-schema>` component defines the expected structure of AI responses:
16
+
17
+ ### Basic Usage
18
+
19
+ ```ruby
20
+ require 'poml'
21
+
22
+ markup = <<~POML
23
+ <poml>
24
+ <role>Data Analyst</role>
25
+ <task>Analyze the provided dataset and generate insights</task>
26
+
27
+ <output-schema name="AnalysisResult">
28
+ {
29
+ "type": "object",
30
+ "properties": {
31
+ "insights": {
32
+ "type": "array",
33
+ "items": {"type": "string"},
34
+ "description": "Key insights from the data"
35
+ },
36
+ "confidence": {
37
+ "type": "number",
38
+ "minimum": 0,
39
+ "maximum": 1,
40
+ "description": "Confidence level of the analysis"
41
+ },
42
+ "recommendations": {
43
+ "type": "array",
44
+ "items": {"type": "string"},
45
+ "description": "Actionable recommendations"
46
+ }
47
+ },
48
+ "required": ["insights", "confidence"]
49
+ }
50
+ </output-schema>
51
+ </poml>
52
+ POML
53
+
54
+ result = Poml.process(markup: markup)
55
+ puts result['metadata']['schemas']
56
+ ```
57
+
58
+ ### Parser Attribute Support
59
+
60
+ POML supports both legacy `lang` and new `parser` attributes:
61
+
62
+ ```ruby
63
+ # New syntax (recommended)
64
+ markup = <<~POML
65
+ <poml>
66
+ <role>Code Reviewer</role>
67
+ <task>Review code and provide feedback</task>
68
+
69
+ <output-schema name="CodeReview" parser="json">
70
+ {
71
+ "type": "object",
72
+ "properties": {
73
+ "issues": {
74
+ "type": "array",
75
+ "items": {
76
+ "type": "object",
77
+ "properties": {
78
+ "severity": {"type": "string", "enum": ["low", "medium", "high"]},
79
+ "description": {"type": "string"},
80
+ "line_number": {"type": "integer"}
81
+ }
82
+ }
83
+ },
84
+ "overall_score": {
85
+ "type": "integer",
86
+ "minimum": 1,
87
+ "maximum": 10
88
+ }
89
+ }
90
+ }
91
+ </output-schema>
92
+ </poml>
93
+ POML
94
+
95
+ # Legacy syntax (still supported)
96
+ markup_legacy = <<~POML
97
+ <poml>
98
+ <output-schema name="CodeReview" lang="json">
99
+ <!-- Same JSON schema -->
100
+ </output-schema>
101
+ </poml>
102
+ POML
103
+ ```
104
+
105
+ ### Eval Parser for Dynamic Schemas
106
+
107
+ Use `parser="eval"` for dynamic schema generation:
108
+
109
+ ```ruby
110
+ markup = <<~POML
111
+ <poml>
112
+ <role>Survey Analyst</role>
113
+ <task>Create survey response schema</task>
114
+
115
+ <output-schema name="SurveyResponse" parser="eval">
116
+ {
117
+ "type": "object",
118
+ "properties": {
119
+ {{#questions}}
120
+ "{{id}}": {
121
+ "type": "{{type}}",
122
+ "description": "{{description}}"
123
+ }{{#unless @last}},{{/unless}}
124
+ {{/questions}}
125
+ }
126
+ }
127
+ </output-schema>
128
+ </poml>
129
+ POML
130
+
131
+ context = {
132
+ 'questions' => [
133
+ { 'id' => 'satisfaction', 'type' => 'integer', 'description' => 'Satisfaction rating 1-10' },
134
+ { 'id' => 'feedback', 'type' => 'string', 'description' => 'Written feedback' },
135
+ { 'id' => 'recommend', 'type' => 'boolean', 'description' => 'Would recommend?' }
136
+ ]
137
+ }
138
+
139
+ result = Poml.process(markup: markup, context: context)
140
+ ```
141
+
142
+ ## Tool Definition Component
143
+
144
+ The `<tool-definition>` component defines AI tools and functions:
145
+
146
+ ### Basic Tool Definition
147
+
148
+ ```ruby
149
+ markup = <<~POML
150
+ <poml>
151
+ <role>Database Assistant</role>
152
+ <task>Help with database queries and analysis</task>
153
+
154
+ <tool-definition name="execute_query">
155
+ {
156
+ "description": "Execute a SQL query on the database",
157
+ "parameters": {
158
+ "type": "object",
159
+ "properties": {
160
+ "query": {
161
+ "type": "string",
162
+ "description": "The SQL query to execute"
163
+ },
164
+ "database": {
165
+ "type": "string",
166
+ "description": "Database name",
167
+ "enum": ["users", "products", "orders"]
168
+ },
169
+ "limit": {
170
+ "type": "integer",
171
+ "description": "Maximum number of rows to return",
172
+ "default": 100,
173
+ "maximum": 1000
174
+ }
175
+ },
176
+ "required": ["query", "database"]
177
+ }
178
+ }
179
+ </tool-definition>
180
+ </poml>
181
+ POML
182
+
183
+ result = Poml.process(markup: markup)
184
+ puts result['tools'] # Tools are now at top level
185
+ ```
186
+
187
+ ### Multiple Tool Definitions
188
+
189
+ ```ruby
190
+ markup = <<~POML
191
+ <poml>
192
+ <role>Data Science Assistant</role>
193
+ <task>Perform data analysis and visualization</task>
194
+
195
+ <tool-definition name="load_dataset">
196
+ {
197
+ "description": "Load a dataset from file or URL",
198
+ "parameters": {
199
+ "type": "object",
200
+ "properties": {
201
+ "source": {"type": "string", "description": "File path or URL"},
202
+ "format": {"type": "string", "enum": ["csv", "json", "excel"]},
203
+ "encoding": {"type": "string", "default": "utf-8"}
204
+ },
205
+ "required": ["source", "format"]
206
+ }
207
+ }
208
+ </tool-definition>
209
+
210
+ <tool-definition name="create_visualization">
211
+ {
212
+ "description": "Create a data visualization chart",
213
+ "parameters": {
214
+ "type": "object",
215
+ "properties": {
216
+ "chart_type": {
217
+ "type": "string",
218
+ "enum": ["bar", "line", "scatter", "histogram", "pie"]
219
+ },
220
+ "x_column": {"type": "string"},
221
+ "y_column": {"type": "string"},
222
+ "title": {"type": "string"},
223
+ "color_scheme": {"type": "string", "default": "viridis"}
224
+ },
225
+ "required": ["chart_type", "x_column"]
226
+ }
227
+ }
228
+ </tool-definition>
229
+ </poml>
230
+ POML
231
+
232
+ result = Poml.process(markup: markup)
233
+ ```
234
+
235
+ ### Enhanced Tool Registration
236
+
237
+ POML supports enhanced tool registration with parameter conversion:
238
+
239
+ ```ruby
240
+ markup = <<~POML
241
+ <poml>
242
+ <role>API Integration Assistant</role>
243
+ <task>Help with API calls and data processing</task>
244
+
245
+ <tool-definition name="api-request" parser="json">
246
+ {
247
+ "description": "Make HTTP API request",
248
+ "parameters": {
249
+ "type": "object",
250
+ "properties": {
251
+ "http-method": {"type": "string", "enum": ["GET", "POST", "PUT", "DELETE"]},
252
+ "endpoint-url": {"type": "string", "format": "uri"},
253
+ "request-headers": {"type": "object"},
254
+ "request-body": {"type": "object"},
255
+ "timeout-seconds": {"type": "integer", "default": 30},
256
+ "retry-attempts": {"type": "integer", "default": 3},
257
+ "follow-redirects": {"type": "boolean", "default": true}
258
+ },
259
+ "required": ["http-method", "endpoint-url"]
260
+ }
261
+ }
262
+ </tool-definition>
263
+ </poml>
264
+ POML
265
+
266
+ result = Poml.process(markup: markup)
267
+
268
+ # The enhanced tool registration will convert:
269
+ # "http-method" -> "httpMethod"
270
+ # "endpoint-url" -> "endpointUrl"
271
+ # "request-headers" -> "requestHeaders"
272
+ # "request-body" -> "requestBody"
273
+ # "timeout-seconds" -> "timeoutSeconds"
274
+ # "retry-attempts" -> "retryAttempts"
275
+ # "follow-redirects" -> "followRedirects"
276
+ ```
277
+
278
+ ## Legacy Meta Components
279
+
280
+ POML maintains backward compatibility with meta-based schema definitions:
281
+
282
+ ### Meta Output Schema
283
+
284
+ ```ruby
285
+ markup = <<~POML
286
+ <poml>
287
+ <role>Report Generator</role>
288
+ <task>Generate performance report</task>
289
+
290
+ <meta type="output-schema" name="PerformanceReport">
291
+ {
292
+ "type": "object",
293
+ "properties": {
294
+ "metrics": {
295
+ "type": "object",
296
+ "properties": {
297
+ "response_time": {"type": "number"},
298
+ "throughput": {"type": "number"},
299
+ "error_rate": {"type": "number"}
300
+ }
301
+ },
302
+ "summary": {"type": "string"},
303
+ "recommendations": {
304
+ "type": "array",
305
+ "items": {"type": "string"}
306
+ }
307
+ }
308
+ }
309
+ </meta>
310
+ </poml>
311
+ POML
312
+
313
+ result = Poml.process(markup: markup)
314
+ ```
315
+
316
+ ### Meta Tool Definition
317
+
318
+ ```ruby
319
+ markup = <<~POML
320
+ <poml>
321
+ <role>File Manager</role>
322
+ <task>Manage files and directories</task>
323
+
324
+ <meta type="tool-definition" name="file_operations">
325
+ {
326
+ "description": "Perform file system operations",
327
+ "parameters": {
328
+ "type": "object",
329
+ "properties": {
330
+ "operation": {
331
+ "type": "string",
332
+ "enum": ["read", "write", "delete", "list", "move"]
333
+ },
334
+ "path": {"type": "string"},
335
+ "content": {"type": "string"},
336
+ "recursive": {"type": "boolean", "default": false}
337
+ },
338
+ "required": ["operation", "path"]
339
+ }
340
+ }
341
+ </meta>
342
+ </poml>
343
+ POML
344
+ ```
345
+
346
+ ## Template Variables in Schemas
347
+
348
+ Use template variables to create dynamic schemas:
349
+
350
+ ```ruby
351
+ markup = <<~POML
352
+ <poml>
353
+ <role>{{domain}} Expert</role>
354
+ <task>Analyze {{entity_type}} data</task>
355
+
356
+ <output-schema name="{{schema_name}}">
357
+ {
358
+ "type": "object",
359
+ "properties": {
360
+ "{{primary_metric}}": {
361
+ "type": "number",
362
+ "description": "Primary {{domain}} metric"
363
+ },
364
+ "categories": {
365
+ "type": "array",
366
+ "items": {
367
+ "type": "string",
368
+ "enum": [{{#categories}}"{{.}}"{{#unless @last}},{{/unless}}{{/categories}}]
369
+ }
370
+ },
371
+ "confidence": {
372
+ "type": "number",
373
+ "minimum": 0,
374
+ "maximum": 1
375
+ }
376
+ },
377
+ "required": ["{{primary_metric}}", "confidence"]
378
+ }
379
+ </output-schema>
380
+ </poml>
381
+ POML
382
+
383
+ context = {
384
+ 'domain' => 'Marketing',
385
+ 'entity_type' => 'campaign',
386
+ 'schema_name' => 'CampaignAnalysis',
387
+ 'primary_metric' => 'conversion_rate',
388
+ 'categories' => ['email', 'social', 'paid', 'organic']
389
+ }
390
+
391
+ result = Poml.process(markup: markup, context: context)
392
+ ```
393
+
394
+ ## Complex Schema Patterns
395
+
396
+ ### Nested Object Schemas
397
+
398
+ ```ruby
399
+ markup = <<~POML
400
+ <poml>
401
+ <role>E-commerce Analyst</role>
402
+ <task>Analyze order data and customer behavior</task>
403
+
404
+ <output-schema name="OrderAnalysis">
405
+ {
406
+ "type": "object",
407
+ "properties": {
408
+ "order_summary": {
409
+ "type": "object",
410
+ "properties": {
411
+ "total_orders": {"type": "integer"},
412
+ "total_revenue": {"type": "number"},
413
+ "average_order_value": {"type": "number"},
414
+ "top_products": {
415
+ "type": "array",
416
+ "items": {
417
+ "type": "object",
418
+ "properties": {
419
+ "product_id": {"type": "string"},
420
+ "name": {"type": "string"},
421
+ "sales_count": {"type": "integer"},
422
+ "revenue": {"type": "number"}
423
+ }
424
+ }
425
+ }
426
+ }
427
+ },
428
+ "customer_insights": {
429
+ "type": "object",
430
+ "properties": {
431
+ "new_customers": {"type": "integer"},
432
+ "returning_customers": {"type": "integer"},
433
+ "customer_segments": {
434
+ "type": "array",
435
+ "items": {
436
+ "type": "object",
437
+ "properties": {
438
+ "segment_name": {"type": "string"},
439
+ "customer_count": {"type": "integer"},
440
+ "avg_order_value": {"type": "number"}
441
+ }
442
+ }
443
+ }
444
+ }
445
+ },
446
+ "recommendations": {
447
+ "type": "array",
448
+ "items": {
449
+ "type": "object",
450
+ "properties": {
451
+ "category": {"type": "string"},
452
+ "priority": {"type": "string", "enum": ["low", "medium", "high"]},
453
+ "action": {"type": "string"},
454
+ "expected_impact": {"type": "string"}
455
+ }
456
+ }
457
+ }
458
+ },
459
+ "required": ["order_summary", "customer_insights", "recommendations"]
460
+ }
461
+ </output-schema>
462
+ </poml>
463
+ POML
464
+ ```
465
+
466
+ ### Conditional Schemas
467
+
468
+ ```ruby
469
+ markup = <<~POML
470
+ <poml>
471
+ <role>{{analysis_type}} Specialist</role>
472
+ <task>Perform {{analysis_type}} analysis</task>
473
+
474
+ <if condition="{{analysis_type}} == 'financial'">
475
+ <output-schema name="FinancialAnalysis">
476
+ {
477
+ "type": "object",
478
+ "properties": {
479
+ "revenue_metrics": {"type": "object"},
480
+ "cost_analysis": {"type": "object"},
481
+ "profitability": {"type": "number"},
482
+ "cash_flow": {"type": "array"}
483
+ }
484
+ }
485
+ </output-schema>
486
+ </if>
487
+
488
+ <if condition="{{analysis_type}} == 'technical'">
489
+ <output-schema name="TechnicalAnalysis">
490
+ {
491
+ "type": "object",
492
+ "properties": {
493
+ "performance_metrics": {"type": "object"},
494
+ "security_assessment": {"type": "object"},
495
+ "scalability_score": {"type": "number"},
496
+ "recommendations": {"type": "array"}
497
+ }
498
+ }
499
+ </output-schema>
500
+ </if>
501
+ </poml>
502
+ POML
503
+ ```
504
+
505
+ ## Integration with Output Formats
506
+
507
+ ### OpenAI Response Format
508
+
509
+ ```ruby
510
+ markup = <<~POML
511
+ <poml>
512
+ <role>Research Assistant</role>
513
+ <task>Summarize research findings</task>
514
+
515
+ <output-schema name="ResearchSummary">
516
+ {
517
+ "type": "object",
518
+ "properties": {
519
+ "key_findings": {"type": "array", "items": {"type": "string"}},
520
+ "methodology": {"type": "string"},
521
+ "confidence_level": {"type": "number"}
522
+ }
523
+ }
524
+ </output-schema>
525
+ </poml>
526
+ POML
527
+
528
+ result = Poml.process(markup: markup, format: 'openaiResponse')
529
+
530
+ puts result['content'] # The prompt
531
+ puts result['metadata']['schemas'] # Schema definitions
532
+ ```
533
+
534
+ ### Pydantic Format
535
+
536
+ ```ruby
537
+ result = Poml.process(markup: markup, format: 'pydantic')
538
+
539
+ puts result['schemas'] # Strict JSON schemas for Python
540
+ puts result['metadata']['python_compatibility'] # Python-specific info
541
+ ```
542
+
543
+ ## Validation and Error Handling
544
+
545
+ ### Schema Validation
546
+
547
+ ```ruby
548
+ def validate_schema_format(markup)
549
+ result = Poml.process(markup: markup)
550
+ schemas = result['metadata']['schemas']
551
+
552
+ schemas.each do |schema|
553
+ # Validate JSON schema format
554
+ begin
555
+ JSON.parse(schema['content']) if schema['content'].is_a?(String)
556
+ puts "✅ Schema '#{schema['name']}' is valid"
557
+ rescue JSON::ParserError => e
558
+ puts "❌ Schema '#{schema['name']}' has invalid JSON: #{e.message}"
559
+ end
560
+ end
561
+ end
562
+ ```
563
+
564
+ ### Tool Definition Validation
565
+
566
+ ```ruby
567
+ def validate_tool_definitions(markup)
568
+ result = Poml.process(markup: markup)
569
+ tools = result['tools'] # Tools are now at top level
570
+
571
+ tools.each do |tool|
572
+ required_fields = ['description', 'parameters']
573
+ missing_fields = required_fields - tool.keys
574
+
575
+ if missing_fields.empty?
576
+ puts "✅ Tool '#{tool['name']}' is complete"
577
+ else
578
+ puts "❌ Tool '#{tool['name']}' missing: #{missing_fields.join(', ')}"
579
+ end
580
+ end
581
+ end
582
+ ```
583
+
584
+ ## Best Practices
585
+
586
+ ### 1. Use Descriptive Schema Names
587
+
588
+ ```ruby
589
+ # Good
590
+ <output-schema name="UserProfileAnalysis">
591
+
592
+ # Better
593
+ <output-schema name="UserBehaviorAnalysisResult">
594
+ ```
595
+
596
+ ### 2. Include Comprehensive Descriptions
597
+
598
+ ```ruby
599
+ {
600
+ "type": "object",
601
+ "properties": {
602
+ "sentiment_score": {
603
+ "type": "number",
604
+ "minimum": -1,
605
+ "maximum": 1,
606
+ "description": "Sentiment analysis score where -1 is very negative, 0 is neutral, and 1 is very positive"
607
+ }
608
+ }
609
+ }
610
+ ```
611
+
612
+ ### 3. Use Validation Constraints
613
+
614
+ ```ruby
615
+ {
616
+ "type": "object",
617
+ "properties": {
618
+ "email": {
619
+ "type": "string",
620
+ "format": "email",
621
+ "description": "Valid email address"
622
+ },
623
+ "age": {
624
+ "type": "integer",
625
+ "minimum": 0,
626
+ "maximum": 150,
627
+ "description": "Age in years"
628
+ },
629
+ "priority": {
630
+ "type": "string",
631
+ "enum": ["low", "medium", "high", "critical"],
632
+ "description": "Priority level"
633
+ }
634
+ }
635
+ }
636
+ ```
637
+
638
+ ### 4. Organize Complex Schemas
639
+
640
+ ```ruby
641
+ # Break complex schemas into logical sections
642
+ {
643
+ "type": "object",
644
+ "properties": {
645
+ "user_data": {
646
+ "type": "object",
647
+ "description": "User profile information",
648
+ "properties": { /* user fields */ }
649
+ },
650
+ "analytics": {
651
+ "type": "object",
652
+ "description": "Analytics and metrics",
653
+ "properties": { /* analytics fields */ }
654
+ },
655
+ "metadata": {
656
+ "type": "object",
657
+ "description": "Processing metadata",
658
+ "properties": { /* metadata fields */ }
659
+ }
660
+ }
661
+ }
662
+ ```
663
+
664
+ ## Next Steps
665
+
666
+ - Learn about [Tool Registration](../advanced/tool-registration.md) for enhanced tool features
667
+ - Explore [Output Formats](../output-formats.md) for schema integration
668
+ - Check [Integration Examples](../integration/rails.md) for real-world usage