poml 0.0.5 → 0.0.6
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/examples/303_new_component_syntax.poml +45 -0
- data/lib/poml/components/base.rb +6 -1
- data/lib/poml/components/meta.rb +60 -4
- data/lib/poml/components/output_schema.rb +85 -0
- data/lib/poml/components/tool_definition.rb +98 -0
- data/lib/poml/components.rb +10 -0
- data/lib/poml/parser.rb +2 -1
- data/lib/poml/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08ad238aa1b84997e3120a4379473c190293c3e6cb4a9fc08fe373457e3d10b7'
|
4
|
+
data.tar.gz: '028e54bb00290ffd9317902101435a2024a15972d6987e4acf9773b00ccba679'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db5eb9e927864afd49fc424be4d97f0e99f206e967605a878da19e8ae150a1ea13b25dab5a4a93773653765933c1e191e4da2c19496d43b6708a37a4aec7e758
|
7
|
+
data.tar.gz: be6a7c9fc5570bc8bc88483f2d7afcf757cf8d8153128b3f3fb6aa90bf54faaaff5b8a2236d68be7ee03692b40b6b5c97d36494f4c1ccb622fea5d96e408514e
|
@@ -0,0 +1,45 @@
|
|
1
|
+
<poml>
|
2
|
+
<!-- New syntax using standalone components -->
|
3
|
+
<output-schema parser="json">
|
4
|
+
{
|
5
|
+
"type": "object",
|
6
|
+
"properties": {
|
7
|
+
"analysis": {
|
8
|
+
"type": "object",
|
9
|
+
"properties": {
|
10
|
+
"sentiment": { "type": "string", "enum": ["positive", "negative", "neutral"] },
|
11
|
+
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
12
|
+
"key_themes": { "type": "array", "items": { "type": "string" } }
|
13
|
+
},
|
14
|
+
"required": ["sentiment", "confidence", "key_themes"]
|
15
|
+
},
|
16
|
+
"summary": { "type": "string" }
|
17
|
+
},
|
18
|
+
"required": ["analysis", "summary"]
|
19
|
+
}
|
20
|
+
</output-schema>
|
21
|
+
|
22
|
+
<tool-definition name="search_web" description="Search the web for information" parser="json">
|
23
|
+
{
|
24
|
+
"type": "object",
|
25
|
+
"properties": {
|
26
|
+
"query": { "type": "string", "description": "Search query" },
|
27
|
+
"max_results": { "type": "integer", "minimum": 1, "maximum": 10, "default": 5 }
|
28
|
+
},
|
29
|
+
"required": ["query"]
|
30
|
+
}
|
31
|
+
</tool-definition>
|
32
|
+
|
33
|
+
<role>Expert Text Analyst</role>
|
34
|
+
|
35
|
+
<task>
|
36
|
+
Analyze the provided text and return structured insights including sentiment analysis,
|
37
|
+
key themes, and a summary. Use the search_web tool if you need additional context.
|
38
|
+
</task>
|
39
|
+
|
40
|
+
<p>Text to analyze: "The new product launch exceeded all expectations with overwhelmingly positive customer feedback and record-breaking sales numbers."</p>
|
41
|
+
|
42
|
+
<output-format>
|
43
|
+
Return your analysis in the structured format defined by the output schema.
|
44
|
+
</output-format>
|
45
|
+
</poml>
|
data/lib/poml/components/base.rb
CHANGED
@@ -162,7 +162,12 @@ module Poml
|
|
162
162
|
COMPONENT_MAPPING = {}
|
163
163
|
|
164
164
|
def self.render_element(element, context)
|
165
|
-
|
165
|
+
# Try to find component using multiple key formats for compatibility
|
166
|
+
tag_name = element.tag_name
|
167
|
+
component_class = COMPONENT_MAPPING[tag_name] ||
|
168
|
+
COMPONENT_MAPPING[tag_name.to_s] ||
|
169
|
+
COMPONENT_MAPPING[tag_name.to_sym] ||
|
170
|
+
TextComponent
|
166
171
|
component = component_class.new(element, context)
|
167
172
|
component.render
|
168
173
|
end
|
data/lib/poml/components/meta.rb
CHANGED
@@ -49,6 +49,12 @@ module Poml
|
|
49
49
|
handle_variables(variables_attr)
|
50
50
|
end
|
51
51
|
|
52
|
+
# Handle tool registration via tool attribute
|
53
|
+
tool_attr = get_attribute('tool')
|
54
|
+
if tool_attr
|
55
|
+
handle_tool_registration_with_name(tool_attr)
|
56
|
+
end
|
57
|
+
|
52
58
|
# Handle general metadata attributes
|
53
59
|
%w[title description author keywords].each do |attr|
|
54
60
|
value = get_attribute(attr)
|
@@ -86,7 +92,7 @@ module Poml
|
|
86
92
|
|
87
93
|
# Auto-detect format if parser_attr is auto
|
88
94
|
if parser_attr == 'auto'
|
89
|
-
parser_attr = content.start_with?('{') ? 'json' : '
|
95
|
+
parser_attr = content.start_with?('{') ? 'json' : 'eval'
|
90
96
|
end
|
91
97
|
|
92
98
|
# Handle new 'eval' parser type as alias for 'expr'
|
@@ -102,6 +108,11 @@ module Poml
|
|
102
108
|
end
|
103
109
|
|
104
110
|
if schema
|
111
|
+
# Check if there's already a response schema defined
|
112
|
+
if @context.response_schema
|
113
|
+
raise Poml::Error, "Multiple response schemas are not allowed. Only one response schema per document is supported."
|
114
|
+
end
|
115
|
+
|
105
116
|
# Store the schema directly for simplicity
|
106
117
|
@context.response_schema = schema
|
107
118
|
end
|
@@ -113,13 +124,58 @@ module Poml
|
|
113
124
|
# Support both old 'lang' and new 'parser' attributes for compatibility
|
114
125
|
parser_attr = get_attribute('parser') || get_attribute('lang', 'auto')
|
115
126
|
|
116
|
-
|
127
|
+
content = @element.content.strip
|
128
|
+
|
129
|
+
# Auto-detect format if parser_attr is auto
|
130
|
+
if parser_attr == 'auto'
|
131
|
+
parser_attr = content.start_with?('{') ? 'json' : 'eval'
|
132
|
+
end
|
133
|
+
|
134
|
+
# Handle new 'eval' parser type as alias for 'expr'
|
135
|
+
parser_attr = 'expr' if parser_attr == 'eval'
|
136
|
+
|
137
|
+
schema = case parser_attr.downcase
|
138
|
+
when 'json'
|
139
|
+
parse_json_schema(content)
|
140
|
+
when 'expr'
|
141
|
+
evaluate_expression_schema(content)
|
142
|
+
else
|
143
|
+
nil
|
144
|
+
end
|
145
|
+
|
146
|
+
if schema
|
147
|
+
@context.tools ||= []
|
148
|
+
|
149
|
+
# If name and description are provided as attributes, use them
|
150
|
+
if name
|
151
|
+
tool_def = {
|
152
|
+
'name' => name,
|
153
|
+
'description' => description
|
154
|
+
}
|
155
|
+
# Merge in the parsed schema (should include parameters, etc.)
|
156
|
+
tool_def.merge!(schema) if schema.is_a?(Hash)
|
157
|
+
elsif schema.is_a?(Hash) && schema['name']
|
158
|
+
# If the schema contains the full tool definition, use it directly
|
159
|
+
tool_def = schema
|
160
|
+
else
|
161
|
+
# No valid tool definition found
|
162
|
+
return
|
163
|
+
end
|
164
|
+
|
165
|
+
@context.tools << tool_def
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def handle_tool_registration_with_name(tool_name)
|
170
|
+
description = get_attribute('description')
|
171
|
+
# Support both old 'lang' and new 'parser' attributes for compatibility
|
172
|
+
parser_attr = get_attribute('parser') || get_attribute('lang', 'auto')
|
117
173
|
|
118
174
|
content = @element.content.strip
|
119
175
|
|
120
176
|
# Auto-detect format if parser_attr is auto
|
121
177
|
if parser_attr == 'auto'
|
122
|
-
parser_attr = content.start_with?('{') ? 'json' : '
|
178
|
+
parser_attr = content.start_with?('{') ? 'json' : 'eval'
|
123
179
|
end
|
124
180
|
|
125
181
|
# Handle new 'eval' parser type as alias for 'expr'
|
@@ -138,7 +194,7 @@ module Poml
|
|
138
194
|
@context.tools ||= []
|
139
195
|
# Store tool with string keys for JSON compatibility
|
140
196
|
tool_def = {
|
141
|
-
'name' =>
|
197
|
+
'name' => tool_name,
|
142
198
|
'description' => description
|
143
199
|
}
|
144
200
|
# Merge in the parsed schema (should include parameters, etc.)
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Poml
|
2
|
+
# OutputSchema component for defining AI response schemas
|
3
|
+
class OutputSchemaComponent < Component
|
4
|
+
def render
|
5
|
+
apply_stylesheet
|
6
|
+
|
7
|
+
# Support both old 'lang' and new 'parser' attributes for compatibility
|
8
|
+
parser_attr = get_attribute('parser') || get_attribute('lang', 'auto')
|
9
|
+
_name = get_attribute('name') # May be used for schema naming in future
|
10
|
+
_description = get_attribute('description') # May be used for schema documentation in future
|
11
|
+
|
12
|
+
content = @element.content.strip
|
13
|
+
|
14
|
+
# Auto-detect format if parser_attr is auto
|
15
|
+
if parser_attr == 'auto'
|
16
|
+
parser_attr = content.start_with?('{') ? 'json' : 'eval'
|
17
|
+
end
|
18
|
+
|
19
|
+
# Handle new 'eval' parser type as alias for 'expr'
|
20
|
+
parser_attr = 'expr' if parser_attr == 'eval'
|
21
|
+
|
22
|
+
schema = case parser_attr.downcase
|
23
|
+
when 'json'
|
24
|
+
parse_json_schema(content)
|
25
|
+
when 'expr'
|
26
|
+
evaluate_expression_schema(content)
|
27
|
+
else
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
if schema
|
32
|
+
# Check if there's already a response schema defined
|
33
|
+
if @context.response_schema
|
34
|
+
raise Poml::Error, "Multiple output-schema elements are not allowed. Only one response schema per document is supported."
|
35
|
+
end
|
36
|
+
|
37
|
+
# Store the schema directly for simplicity
|
38
|
+
@context.response_schema = schema
|
39
|
+
end
|
40
|
+
|
41
|
+
# Meta-like components don't produce output
|
42
|
+
''
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def parse_json_schema(content)
|
48
|
+
# Apply template substitution first
|
49
|
+
substituted_content = @context.template_engine.substitute(content)
|
50
|
+
|
51
|
+
begin
|
52
|
+
JSON.parse(substituted_content)
|
53
|
+
rescue JSON::ParserError => e
|
54
|
+
raise Poml::Error, "Invalid JSON schema: #{e.message}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def evaluate_expression_schema(content)
|
59
|
+
# Apply template substitution first
|
60
|
+
substituted_content = @context.template_engine.substitute(content)
|
61
|
+
|
62
|
+
begin
|
63
|
+
# In a real implementation, this would evaluate JavaScript expressions
|
64
|
+
# For now, we'll try to parse as JSON if it looks like JSON,
|
65
|
+
# otherwise treat it as a placeholder
|
66
|
+
if substituted_content.strip.start_with?('{', '[')
|
67
|
+
JSON.parse(substituted_content)
|
68
|
+
else
|
69
|
+
# This would need a JavaScript engine in a real implementation
|
70
|
+
# For now, return a placeholder that indicates expression evaluation
|
71
|
+
{
|
72
|
+
"_expression" => substituted_content,
|
73
|
+
"_note" => "Expression evaluation not implemented in Ruby gem"
|
74
|
+
}
|
75
|
+
end
|
76
|
+
rescue JSON::ParserError
|
77
|
+
# Return expression as-is if it can't be parsed as JSON
|
78
|
+
{
|
79
|
+
"_expression" => substituted_content,
|
80
|
+
"_note" => "Expression evaluation not implemented in Ruby gem"
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Poml
|
2
|
+
# ToolDefinition component for registering AI tools
|
3
|
+
class ToolDefinitionComponent < Component
|
4
|
+
def render
|
5
|
+
apply_stylesheet
|
6
|
+
|
7
|
+
name = get_attribute('name')
|
8
|
+
description = get_attribute('description')
|
9
|
+
# Support both old 'lang' and new 'parser' attributes for compatibility
|
10
|
+
parser_attr = get_attribute('parser') || get_attribute('lang', 'auto')
|
11
|
+
|
12
|
+
return '' unless name # Name is required for tools
|
13
|
+
|
14
|
+
content = @element.content.strip
|
15
|
+
|
16
|
+
# Auto-detect format if parser_attr is auto
|
17
|
+
if parser_attr == 'auto'
|
18
|
+
parser_attr = content.start_with?('{') ? 'json' : 'eval'
|
19
|
+
end
|
20
|
+
|
21
|
+
# Handle new 'eval' parser type as alias for 'expr'
|
22
|
+
parser_attr = 'expr' if parser_attr == 'eval'
|
23
|
+
|
24
|
+
schema = case parser_attr.downcase
|
25
|
+
when 'json'
|
26
|
+
parse_json_schema(content)
|
27
|
+
when 'expr'
|
28
|
+
evaluate_expression_schema(content)
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
if schema
|
34
|
+
@context.tools ||= []
|
35
|
+
# Store tool with string keys for JSON compatibility
|
36
|
+
tool_def = {
|
37
|
+
'name' => name,
|
38
|
+
'description' => description,
|
39
|
+
'schema' => schema.is_a?(String) ? schema : JSON.generate(schema)
|
40
|
+
}
|
41
|
+
|
42
|
+
# For compatibility with expected structure, also store as parameters
|
43
|
+
if schema.is_a?(Hash)
|
44
|
+
tool_def['parameters'] = schema
|
45
|
+
end
|
46
|
+
|
47
|
+
@context.tools << tool_def
|
48
|
+
end
|
49
|
+
|
50
|
+
# Meta-like components don't produce output
|
51
|
+
''
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def parse_json_schema(content)
|
57
|
+
# Apply template substitution first
|
58
|
+
substituted_content = @context.template_engine.substitute(content)
|
59
|
+
|
60
|
+
begin
|
61
|
+
parsed = JSON.parse(substituted_content)
|
62
|
+
# Return the parsed schema directly - don't wrap it
|
63
|
+
parsed
|
64
|
+
rescue JSON::ParserError => e
|
65
|
+
raise Poml::Error, "Invalid JSON schema for tool '#{get_attribute('name')}': #{e.message}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def evaluate_expression_schema(content)
|
70
|
+
# Apply template substitution first
|
71
|
+
substituted_content = @context.template_engine.substitute(content)
|
72
|
+
|
73
|
+
begin
|
74
|
+
# In a real implementation, this would evaluate JavaScript expressions
|
75
|
+
# For now, we'll try to parse as JSON if it looks like JSON,
|
76
|
+
# otherwise treat it as a placeholder
|
77
|
+
if substituted_content.strip.start_with?('{', '[')
|
78
|
+
parsed = JSON.parse(substituted_content)
|
79
|
+
# Return the parsed schema directly - don't wrap it
|
80
|
+
parsed
|
81
|
+
else
|
82
|
+
# This would need a JavaScript engine in a real implementation
|
83
|
+
# For now, return a placeholder that indicates expression evaluation
|
84
|
+
{
|
85
|
+
"_expression" => substituted_content,
|
86
|
+
"_note" => "Expression evaluation not implemented in Ruby gem"
|
87
|
+
}
|
88
|
+
end
|
89
|
+
rescue JSON::ParserError
|
90
|
+
# Return expression as-is if it can't be parsed as JSON
|
91
|
+
{
|
92
|
+
"_expression" => substituted_content,
|
93
|
+
"_note" => "Expression evaluation not implemented in Ruby gem"
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/poml/components.rb
CHANGED
@@ -14,6 +14,8 @@ require_relative 'components/media'
|
|
14
14
|
require_relative 'components/utilities'
|
15
15
|
require_relative 'components/meta'
|
16
16
|
require_relative 'components/template'
|
17
|
+
require_relative 'components/output_schema'
|
18
|
+
require_relative 'components/tool_definition'
|
17
19
|
|
18
20
|
module Poml
|
19
21
|
# Update the component mapping after all components are loaded
|
@@ -126,6 +128,14 @@ module Poml
|
|
126
128
|
# Meta components
|
127
129
|
meta: MetaComponent,
|
128
130
|
Meta: MetaComponent,
|
131
|
+
'output-schema': OutputSchemaComponent,
|
132
|
+
'outputschema': OutputSchemaComponent,
|
133
|
+
OutputSchema: OutputSchemaComponent,
|
134
|
+
'tool-definition': ToolDefinitionComponent,
|
135
|
+
'tooldefinition': ToolDefinitionComponent,
|
136
|
+
ToolDefinition: ToolDefinitionComponent,
|
137
|
+
tool: ToolDefinitionComponent, # 'tool' is an alias for 'tool-definition'
|
138
|
+
Tool: ToolDefinitionComponent,
|
129
139
|
|
130
140
|
# Template components
|
131
141
|
include: IncludeComponent,
|
data/lib/poml/parser.rb
CHANGED
@@ -236,7 +236,8 @@ module Poml
|
|
236
236
|
def preprocess_void_elements(content)
|
237
237
|
# List of HTML void elements that should be self-closing in XML
|
238
238
|
# Note: 'meta' is removed from this list because POML meta components can have content
|
239
|
-
|
239
|
+
# Note: 'input' is removed because POML input components (for examples) can have content
|
240
|
+
void_elements = %w[br hr img area base col embed link param source track wbr]
|
240
241
|
|
241
242
|
# Convert <element> to <element/> for void elements, but only if not already self-closing
|
242
243
|
void_elements.each do |element|
|
data/lib/poml/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: poml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ghennadii Mirosnicenco
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-08-
|
10
|
+
date: 2025-08-22 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rexml
|
@@ -64,6 +64,7 @@ files:
|
|
64
64
|
- examples/301_generate_poml.poml
|
65
65
|
- examples/301_new_schema_syntax.poml
|
66
66
|
- examples/302_schema_compatibility.poml
|
67
|
+
- examples/303_new_component_syntax.poml
|
67
68
|
- examples/README.md
|
68
69
|
- examples/assets/101_jerry_mouse.jpg
|
69
70
|
- examples/assets/101_tom_and_jerry.docx
|
@@ -110,9 +111,11 @@ files:
|
|
110
111
|
- lib/poml/components/lists.rb
|
111
112
|
- lib/poml/components/media.rb
|
112
113
|
- lib/poml/components/meta.rb
|
114
|
+
- lib/poml/components/output_schema.rb
|
113
115
|
- lib/poml/components/styling.rb
|
114
116
|
- lib/poml/components/template.rb
|
115
117
|
- lib/poml/components/text.rb
|
118
|
+
- lib/poml/components/tool_definition.rb
|
116
119
|
- lib/poml/components/utilities.rb
|
117
120
|
- lib/poml/components/workflow.rb
|
118
121
|
- lib/poml/components_new.rb
|