poml 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +239 -0
  4. data/TUTORIAL.md +987 -0
  5. data/bin/poml +80 -0
  6. data/examples/101_explain_character.poml +30 -0
  7. data/examples/102_render_xml.poml +40 -0
  8. data/examples/103_word_todos.poml +27 -0
  9. data/examples/104_financial_analysis.poml +33 -0
  10. data/examples/105_write_blog_post.poml +48 -0
  11. data/examples/106_research.poml +36 -0
  12. data/examples/107_read_report_pdf.poml +4 -0
  13. data/examples/201_orders_qa.poml +50 -0
  14. data/examples/202_arc_agi.poml +36 -0
  15. data/examples/301_generate_poml.poml +46 -0
  16. data/examples/README.md +50 -0
  17. data/examples/_generate_expects.py +35 -0
  18. data/examples/assets/101_jerry_mouse.jpg +0 -0
  19. data/examples/assets/101_tom_and_jerry.docx +0 -0
  20. data/examples/assets/101_tom_cat.jpg +0 -0
  21. data/examples/assets/101_tom_introduction.txt +9 -0
  22. data/examples/assets/103_prompt_wizard.docx +0 -0
  23. data/examples/assets/104_chart_normalized_price.png +0 -0
  24. data/examples/assets/104_chart_price.png +0 -0
  25. data/examples/assets/104_mag7.xlsx +0 -0
  26. data/examples/assets/107_usenix_paper.pdf +0 -0
  27. data/examples/assets/201_order_instructions.json +7 -0
  28. data/examples/assets/201_orderlines.csv +2 -0
  29. data/examples/assets/201_orders.csv +3 -0
  30. data/examples/assets/202_arc_agi_data.json +1 -0
  31. data/examples/expects/101_explain_character.txt +117 -0
  32. data/examples/expects/102_render_xml.txt +28 -0
  33. data/examples/expects/103_word_todos.txt +121 -0
  34. data/examples/expects/104_financial_analysis.txt +86 -0
  35. data/examples/expects/105_write_blog_post.txt +41 -0
  36. data/examples/expects/106_research.txt +29 -0
  37. data/examples/expects/107_read_report_pdf.txt +151 -0
  38. data/examples/expects/201_orders_qa.txt +44 -0
  39. data/examples/expects/202_arc_agi.txt +64 -0
  40. data/examples/expects/301_generate_poml.txt +153 -0
  41. data/examples/ruby_expects/101_explain_character.txt +17 -0
  42. data/examples/ruby_expects/102_render_xml.txt +28 -0
  43. data/examples/ruby_expects/103_word_todos.txt +14 -0
  44. data/examples/ruby_expects/104_financial_analysis.txt +0 -0
  45. data/examples/ruby_expects/105_write_blog_post.txt +57 -0
  46. data/examples/ruby_expects/106_research.txt +5 -0
  47. data/examples/ruby_expects/107_read_report_pdf.txt +403 -0
  48. data/examples/ruby_expects/201_orders_qa.txt +41 -0
  49. data/examples/ruby_expects/202_arc_agi.txt +17 -0
  50. data/examples/ruby_expects/301_generate_poml.txt +17 -0
  51. data/lib/poml/components/base.rb +132 -0
  52. data/lib/poml/components/content.rb +156 -0
  53. data/lib/poml/components/data.rb +346 -0
  54. data/lib/poml/components/examples.rb +55 -0
  55. data/lib/poml/components/instructions.rb +93 -0
  56. data/lib/poml/components/layout.rb +50 -0
  57. data/lib/poml/components/lists.rb +82 -0
  58. data/lib/poml/components/styling.rb +36 -0
  59. data/lib/poml/components/text.rb +8 -0
  60. data/lib/poml/components/workflow.rb +63 -0
  61. data/lib/poml/components.rb +47 -0
  62. data/lib/poml/components_new.rb +297 -0
  63. data/lib/poml/components_old.rb +1096 -0
  64. data/lib/poml/context.rb +53 -0
  65. data/lib/poml/parser.rb +153 -0
  66. data/lib/poml/renderer.rb +147 -0
  67. data/lib/poml/template_engine.rb +66 -0
  68. data/lib/poml/version.rb +5 -0
  69. data/lib/poml.rb +53 -0
  70. data/media/logo-16-purple.png +0 -0
  71. data/media/logo-64-white.png +0 -0
  72. metadata +149 -0
@@ -0,0 +1,53 @@
1
+ require 'json'
2
+
3
+ module Poml
4
+ # Context object that holds variables, stylesheets, and processing state
5
+ class Context
6
+ attr_accessor :variables, :stylesheet, :chat, :texts, :source_path, :syntax, :header_level
7
+
8
+ def initialize(variables: {}, stylesheet: nil, chat: true, syntax: nil)
9
+ @variables = variables || {}
10
+ @stylesheet = parse_stylesheet(stylesheet)
11
+ @chat = chat
12
+ @texts = {}
13
+ @source_path = nil
14
+ @syntax = syntax
15
+ @header_level = 1 # Track current header nesting level
16
+ end
17
+
18
+ def xml_mode?
19
+ @syntax == 'xml'
20
+ end
21
+
22
+ def determine_syntax(element)
23
+ # Check if element or parent has syntax specified
24
+ element_syntax = element.attributes['syntax'] if element.respond_to?(:attributes)
25
+ element_syntax || @syntax || 'markdown'
26
+ end
27
+
28
+ def with_increased_header_level
29
+ old_level = @header_level
30
+ @header_level += 1
31
+ yield
32
+ ensure
33
+ @header_level = old_level
34
+ end
35
+
36
+ private
37
+
38
+ def parse_stylesheet(stylesheet)
39
+ case stylesheet
40
+ when Hash
41
+ stylesheet
42
+ when String
43
+ JSON.parse(stylesheet)
44
+ when nil
45
+ {}
46
+ else
47
+ {}
48
+ end
49
+ rescue JSON::ParserError
50
+ {}
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,153 @@
1
+ require 'rexml/document'
2
+
3
+ module Poml
4
+ # Element represents a parsed POML element
5
+ class Element
6
+ attr_accessor :tag_name, :attributes, :content, :children
7
+
8
+ def initialize(tag_name:, attributes: {}, content: '', children: [])
9
+ @tag_name = tag_name
10
+ @attributes = attributes || {}
11
+ @content = content || ''
12
+ @children = children || []
13
+ end
14
+
15
+ def text?
16
+ @tag_name == :text
17
+ end
18
+
19
+ def component?
20
+ !text?
21
+ end
22
+ end
23
+
24
+ # Parser for POML markup
25
+ class Parser
26
+ def initialize(context)
27
+ @context = context
28
+ @template_engine = TemplateEngine.new(context)
29
+ end
30
+
31
+ def parse(content)
32
+ # Handle escape characters
33
+ content = unescape_poml(content)
34
+
35
+ # Remove XML comments but preserve surrounding whitespace
36
+ content = content.gsub(/(\s*)<!--.*?-->(\s*)/m) do |match|
37
+ before_space = $1
38
+ after_space = $2
39
+ # Preserve the original spacing pattern, but normalize to a single space if there was whitespace
40
+ if before_space.length > 0 || after_space.length > 0
41
+ ' '
42
+ else
43
+ ''
44
+ end
45
+ end
46
+
47
+ # Apply template substitutions
48
+ content = @template_engine.substitute(content)
49
+
50
+ # Check if content is wrapped in <poml> tags and extract syntax
51
+ content = content.strip
52
+ if content =~ /\A<poml\b([^>]*)?>(.*)<\/poml>\s*\z/m
53
+ poml_attributes = $1
54
+ content = $2
55
+
56
+ # Extract syntax attribute if present
57
+ if poml_attributes && poml_attributes =~ /syntax\s*=\s*["']([^"']+)["']/
58
+ @context.syntax = $1
59
+ end
60
+ end
61
+
62
+ # Pre-process stylesheets before parsing other components
63
+ preprocess_stylesheets(content)
64
+
65
+ # Parse as XML with whitespace preservation
66
+ begin
67
+ doc = REXML::Document.new("<root>#{content}</root>")
68
+ # Preserve whitespace in text nodes
69
+ doc.context[:ignore_whitespace_nodes] = :none
70
+ parse_element(doc.root)
71
+ rescue => e
72
+ # If XML parsing fails, treat as plain text
73
+ puts "XML parsing failed: #{e.message}" if defined?(DEBUG)
74
+ [Element.new(tag_name: :text, content: content)]
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ def parse_element(xml_element)
81
+ elements = []
82
+
83
+ xml_element.children.each do |child|
84
+ case child
85
+ when REXML::Text
86
+ text_content = child.to_s
87
+ next if text_content.strip.empty? # Only skip if completely empty when stripped
88
+
89
+ # Check if the content (after removing newlines) ends with a space
90
+ content_no_newlines = text_content.gsub(/\n/, ' ')
91
+ preserves_trailing_space = content_no_newlines.rstrip != content_no_newlines
92
+
93
+ # Normalize the text: strip leading/trailing whitespace
94
+ normalized = text_content.strip
95
+
96
+ # Add back trailing space if it was significant (i.e., there was a space before newlines)
97
+ normalized += ' ' if preserves_trailing_space
98
+
99
+ next if normalized.empty?
100
+ elements << Element.new(tag_name: :text, content: normalized)
101
+ when REXML::Element
102
+ # Convert REXML attributes to string hash
103
+ attrs = {}
104
+ child.attributes.each do |name, value|
105
+ attrs[name.downcase] = value.to_s
106
+ end
107
+
108
+ elements << Element.new(
109
+ tag_name: child.name.downcase.to_sym,
110
+ attributes: attrs,
111
+ content: extract_text_content(child),
112
+ children: parse_element(child)
113
+ )
114
+ end
115
+ end
116
+
117
+ elements
118
+ end
119
+
120
+ def extract_text_content(xml_element)
121
+ # Extract direct text content (not from child elements)
122
+ text_nodes = xml_element.children.select { |child| child.is_a?(REXML::Text) }
123
+ text_nodes.map(&:to_s).join('').strip
124
+ end
125
+
126
+ def unescape_poml(text)
127
+ # Handle POML escape characters
128
+ text.gsub(/#quot;/, '"')
129
+ .gsub(/#apos;/, "'")
130
+ .gsub(/#amp;/, '&')
131
+ .gsub(/#lt;/, '<')
132
+ .gsub(/#gt;/, '>')
133
+ .gsub(/#hash;/, '#')
134
+ .gsub(/#lbrace;/, '{')
135
+ .gsub(/#rbrace;/, '}')
136
+ end
137
+
138
+ def preprocess_stylesheets(content)
139
+ # Extract and process stylesheet elements before main parsing
140
+ content.scan(/<stylesheet\b[^>]*>(.*?)<\/stylesheet>/m) do |stylesheet_content|
141
+ begin
142
+ stylesheet_text = stylesheet_content[0].strip
143
+ if stylesheet_text.start_with?('{') && stylesheet_text.end_with?('}')
144
+ stylesheet = JSON.parse(stylesheet_text)
145
+ @context.stylesheet.merge!(stylesheet) if stylesheet.is_a?(Hash)
146
+ end
147
+ rescue => e
148
+ # Silently fail JSON parsing errors
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,147 @@
1
+ require 'json'
2
+
3
+ module Poml
4
+ # Renderer converts parsed POML elements to various output formats
5
+ class Renderer
6
+ def initialize(context)
7
+ @context = context
8
+ end
9
+
10
+ def render(elements, format = 'dict')
11
+ case format
12
+ when 'raw'
13
+ render_raw(elements)
14
+ when 'dict'
15
+ render_dict(elements)
16
+ when 'openai_chat'
17
+ render_openai_chat(elements)
18
+ when 'langchain'
19
+ render_langchain(elements)
20
+ when 'pydantic'
21
+ render_pydantic(elements)
22
+ else
23
+ raise Error, "Unknown format: #{format}"
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def render_raw(elements)
30
+ content = elements.map { |element| Components.render_element(element, @context) }.join('')
31
+
32
+ # For raw format, wrap in message boundaries like Python package does
33
+ if @context.chat && !content.strip.empty?
34
+ # Determine if this should be system or human message
35
+ # If it contains role/task/instructions, it's typically a system message
36
+ # Otherwise, it's a human message
37
+ message_type = determine_message_type(elements)
38
+ "===== #{message_type} =====\n\n#{content.strip}\n"
39
+ else
40
+ content
41
+ end
42
+ end
43
+
44
+ def render_dict(elements)
45
+ {
46
+ 'content' => render_raw(elements),
47
+ 'metadata' => {
48
+ 'chat' => @context.chat,
49
+ 'stylesheet' => @context.stylesheet,
50
+ 'variables' => @context.variables
51
+ }
52
+ }
53
+ end
54
+
55
+ def render_openai_chat(elements)
56
+ content = render_raw(elements)
57
+ if @context.chat
58
+ parse_chat_messages(content)
59
+ else
60
+ [{ 'role' => 'user', 'content' => content }]
61
+ end
62
+ end
63
+
64
+ def render_langchain(elements)
65
+ content = render_raw(elements)
66
+ {
67
+ 'messages' => render_openai_chat(elements),
68
+ 'content' => content
69
+ }
70
+ end
71
+
72
+ def render_pydantic(elements)
73
+ # Simplified pydantic-like structure
74
+ {
75
+ 'prompt' => render_raw(elements),
76
+ 'variables' => @context.variables,
77
+ 'chat_enabled' => @context.chat
78
+ }
79
+ end
80
+
81
+ def parse_chat_messages(content)
82
+ # Simplified chat message parsing
83
+ # In a full implementation, this would be more sophisticated
84
+ messages = []
85
+ current_role = 'user'
86
+ current_content = ''
87
+
88
+ content.split(/\n\n+/).each do |section|
89
+ section = section.strip
90
+ next if section.empty?
91
+
92
+ # Simple heuristic: if section starts with certain patterns, it might be assistant content
93
+ if section.start_with?('**Output:**', '**Assistant:**', 'Response:')
94
+ if !current_content.empty?
95
+ messages << { 'role' => current_role, 'content' => current_content.strip }
96
+ end
97
+ current_role = 'assistant'
98
+ current_content = section.gsub(/^\*\*(Output|Assistant):\*\*\s*/, '').gsub(/^Response:\s*/, '')
99
+ else
100
+ current_content += (current_content.empty? ? '' : "\n\n") + section
101
+ end
102
+ end
103
+
104
+ if !current_content.empty?
105
+ messages << { 'role' => current_role, 'content' => current_content.strip }
106
+ end
107
+
108
+ messages.empty? ? [{ 'role' => 'user', 'content' => content }] : messages
109
+ end
110
+
111
+ def determine_message_type(elements)
112
+ # Extract tag names from elements
113
+ tag_names = elements.map(&:tag_name).compact
114
+
115
+ # Check for system-oriented components
116
+ has_role = tag_names.include?(:role)
117
+ has_task = tag_names.include?(:task)
118
+ has_hint = tag_names.include?(:hint)
119
+
120
+ # Check for human-oriented components or content that suggests user interaction
121
+ has_document = tag_names.include?(:document) || tag_names.include?('Document')
122
+ has_question_content = elements.any? { |el|
123
+ el.respond_to?(:content) && el.content&.match?(/\b(what|how|why|when|where|please|can you)\b/i)
124
+ }
125
+
126
+ # System message detection rules based on original TypeScript implementation:
127
+ # 1. If it's only role/task/hint components → system message
128
+ # 2. If it has role AND task → system message (instruction setup)
129
+ # 3. If it has role but also user interaction content → human message
130
+ # 4. If it has document import or question-like content → human message
131
+
132
+ if has_role && has_task
133
+ # Role + Task = system instruction setup
134
+ 'system'
135
+ elsif has_role && !has_document && !has_question_content && tag_names.all? { |tag| [:role, :task, :hint, :text, :p].include?(tag) }
136
+ # Pure role/task/hint setup without user content
137
+ 'system'
138
+ elsif has_document || has_question_content
139
+ # Document import or question-like content = human message
140
+ 'human'
141
+ else
142
+ # Default to human for mixed or ambiguous content
143
+ 'human'
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,66 @@
1
+ module Poml
2
+ # Template engine for handling {{variable}} substitutions
3
+ class TemplateEngine
4
+ def initialize(context)
5
+ @context = context
6
+ end
7
+
8
+ def substitute(text)
9
+ return text unless text.is_a?(String)
10
+
11
+ # Handle {{variable}} substitutions
12
+ text.gsub(/\{\{(.+?)\}\}/) do |match|
13
+ expression = $1.strip
14
+ evaluate_expression(expression)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def evaluate_expression(expression)
21
+ # Handle dot notation and arithmetic expressions
22
+
23
+ # Simple arithmetic operations like loop.index+1
24
+ if expression =~ /^(\w+(?:\.\w+)*)\s*\+\s*(\d+)$/
25
+ variable_path = $1
26
+ increment = $2.to_i
27
+
28
+ value = get_nested_variable(variable_path)
29
+ if value.is_a?(Numeric)
30
+ (value + increment).to_s
31
+ else
32
+ "{{#{expression}}}"
33
+ end
34
+ elsif expression =~ /^(\w+(?:\.\w+)*)$/
35
+ # Simple variable or dot notation lookup
36
+ variable_path = $1
37
+ value = get_nested_variable(variable_path)
38
+ value ? value.to_s : "{{#{expression}}}"
39
+ elsif @context.variables.key?(expression)
40
+ # Direct variable lookup (backward compatibility)
41
+ @context.variables[expression].to_s
42
+ else
43
+ # Return original expression if not found
44
+ "{{#{expression}}}"
45
+ end
46
+ end
47
+
48
+ def get_nested_variable(path)
49
+ # Handle dot notation like "loop.index"
50
+ parts = path.split('.')
51
+ value = @context.variables
52
+
53
+ parts.each do |part|
54
+ if value.is_a?(Hash) && value.key?(part)
55
+ value = value[part]
56
+ elsif value.is_a?(Hash) && value.key?(part.to_sym)
57
+ value = value[part.to_sym]
58
+ else
59
+ return nil
60
+ end
61
+ end
62
+
63
+ value
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Poml
4
+ VERSION = "0.0.1"
5
+ end
data/lib/poml.rb ADDED
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "poml/version"
4
+ require_relative 'poml/context'
5
+ require_relative 'poml/parser'
6
+ require_relative 'poml/renderer'
7
+ require_relative 'poml/components'
8
+ require_relative 'poml/template_engine'
9
+
10
+ module Poml
11
+ class Error < StandardError; end
12
+
13
+ # Main entry point for processing POML files
14
+ # Parameters:
15
+ # - markup: POML file path or string content
16
+ # - context: Hash of variables for template substitution
17
+ # - stylesheet: Optional stylesheet as string or hash
18
+ # - chat: Boolean indicating chat format (default: true)
19
+ # - output_file: Path to save output (optional)
20
+ # - format: 'raw', 'dict', 'openai_chat', 'langchain', 'pydantic' (default: 'dict')
21
+ # - extra_args: Array of extra CLI args (ignored in pure Ruby implementation)
22
+ def self.process(markup:, context: nil, stylesheet: nil, chat: true, output_file: nil, format: 'dict', extra_args: [])
23
+ # Create POML context
24
+ poml_context = Context.new(
25
+ variables: context || {},
26
+ stylesheet: stylesheet,
27
+ chat: chat
28
+ )
29
+
30
+ # Read markup content and set source path
31
+ content = if File.exist?(markup.to_s)
32
+ poml_context.source_path = File.expand_path(markup.to_s)
33
+ File.read(markup)
34
+ else
35
+ markup.to_s
36
+ end
37
+
38
+ # Parse POML content
39
+ parser = Parser.new(poml_context)
40
+ parsed_elements = parser.parse(content)
41
+
42
+ # Render to the desired format
43
+ renderer = Renderer.new(poml_context)
44
+ result = renderer.render(parsed_elements, format)
45
+
46
+ # Save to file if specified
47
+ if output_file
48
+ File.write(output_file, result)
49
+ end
50
+
51
+ result
52
+ end
53
+ end
Binary file
Binary file
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: poml
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ghennadii Mirosnicenco
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-08-17 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rexml
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '3.2'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '3.2'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rubyzip
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.3'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.3'
40
+ description: "POML is a Ruby gem that implements POML (Prompt Oriented Markup Language),
41
+ \na markup language for structured prompt engineering. This is a Ruby port of \nthe
42
+ original Microsoft POML library, providing comprehensive tools for creating, \nprocessing,
43
+ and rendering structured prompts with support for multiple output \nformats including
44
+ OpenAI Chat, LangChain, and Pydantic.\n"
45
+ email:
46
+ - linkator7@gmail.com
47
+ executables:
48
+ - poml
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - LICENSE.txt
53
+ - README.md
54
+ - TUTORIAL.md
55
+ - bin/poml
56
+ - examples/101_explain_character.poml
57
+ - examples/102_render_xml.poml
58
+ - examples/103_word_todos.poml
59
+ - examples/104_financial_analysis.poml
60
+ - examples/105_write_blog_post.poml
61
+ - examples/106_research.poml
62
+ - examples/107_read_report_pdf.poml
63
+ - examples/201_orders_qa.poml
64
+ - examples/202_arc_agi.poml
65
+ - examples/301_generate_poml.poml
66
+ - examples/README.md
67
+ - examples/_generate_expects.py
68
+ - examples/assets/101_jerry_mouse.jpg
69
+ - examples/assets/101_tom_and_jerry.docx
70
+ - examples/assets/101_tom_cat.jpg
71
+ - examples/assets/101_tom_introduction.txt
72
+ - examples/assets/103_prompt_wizard.docx
73
+ - examples/assets/104_chart_normalized_price.png
74
+ - examples/assets/104_chart_price.png
75
+ - examples/assets/104_mag7.xlsx
76
+ - examples/assets/107_usenix_paper.pdf
77
+ - examples/assets/201_order_instructions.json
78
+ - examples/assets/201_orderlines.csv
79
+ - examples/assets/201_orders.csv
80
+ - examples/assets/202_arc_agi_data.json
81
+ - examples/expects/101_explain_character.txt
82
+ - examples/expects/102_render_xml.txt
83
+ - examples/expects/103_word_todos.txt
84
+ - examples/expects/104_financial_analysis.txt
85
+ - examples/expects/105_write_blog_post.txt
86
+ - examples/expects/106_research.txt
87
+ - examples/expects/107_read_report_pdf.txt
88
+ - examples/expects/201_orders_qa.txt
89
+ - examples/expects/202_arc_agi.txt
90
+ - examples/expects/301_generate_poml.txt
91
+ - examples/ruby_expects/101_explain_character.txt
92
+ - examples/ruby_expects/102_render_xml.txt
93
+ - examples/ruby_expects/103_word_todos.txt
94
+ - examples/ruby_expects/104_financial_analysis.txt
95
+ - examples/ruby_expects/105_write_blog_post.txt
96
+ - examples/ruby_expects/106_research.txt
97
+ - examples/ruby_expects/107_read_report_pdf.txt
98
+ - examples/ruby_expects/201_orders_qa.txt
99
+ - examples/ruby_expects/202_arc_agi.txt
100
+ - examples/ruby_expects/301_generate_poml.txt
101
+ - lib/poml.rb
102
+ - lib/poml/components.rb
103
+ - lib/poml/components/base.rb
104
+ - lib/poml/components/content.rb
105
+ - lib/poml/components/data.rb
106
+ - lib/poml/components/examples.rb
107
+ - lib/poml/components/instructions.rb
108
+ - lib/poml/components/layout.rb
109
+ - lib/poml/components/lists.rb
110
+ - lib/poml/components/styling.rb
111
+ - lib/poml/components/text.rb
112
+ - lib/poml/components/workflow.rb
113
+ - lib/poml/components_new.rb
114
+ - lib/poml/components_old.rb
115
+ - lib/poml/context.rb
116
+ - lib/poml/parser.rb
117
+ - lib/poml/renderer.rb
118
+ - lib/poml/template_engine.rb
119
+ - lib/poml/version.rb
120
+ - media/logo-16-purple.png
121
+ - media/logo-64-white.png
122
+ homepage: https://github.com/GhennadiiMir/poml
123
+ licenses:
124
+ - MIT
125
+ metadata:
126
+ homepage_uri: https://github.com/GhennadiiMir/poml
127
+ source_code_uri: https://github.com/GhennadiiMir/poml
128
+ documentation_uri: https://github.com/GhennadiiMir/poml/blob/main/TUTORIAL.md
129
+ bug_tracker_uri: https://github.com/GhennadiiMir/poml/issues
130
+ changelog_uri: https://github.com/GhennadiiMir/poml/releases
131
+ rubygems_mfa_required: 'true'
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: 2.7.0
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubygems_version: 3.6.5
147
+ specification_version: 4
148
+ summary: Ruby implementation of POML (Prompt Oriented Markup Language)
149
+ test_files: []