artificial 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.
- checksums.yaml +7 -0
- data/README.md +303 -0
- data/Rakefile +101 -0
- data/examples/demo.rb +138 -0
- data/lib/artificial/parsers/json_parser.rb +103 -0
- data/lib/artificial/parsers/string_parser.rb +31 -0
- data/lib/artificial/parsers/xml_parser.rb +132 -0
- data/lib/artificial/parsers/yaml_parser.rb +87 -0
- data/lib/artificial/prompt.rb +440 -0
- data/lib/artificial/validators/message_validator.rb +72 -0
- data/lib/artificial/validators/role_validator.rb +128 -0
- data/lib/artificial/version.rb +5 -0
- data/lib/artificial.rb +68 -0
- data/sig/artificial.rbs +4 -0
- metadata +155 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rexml/document'
|
4
|
+
|
5
|
+
module Artificial
|
6
|
+
module Parsers
|
7
|
+
class XMLParser
|
8
|
+
attr_reader :input, :parsed_data, :errors
|
9
|
+
|
10
|
+
def initialize(input)
|
11
|
+
@input = input
|
12
|
+
@parsed_data = {}
|
13
|
+
@errors = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse
|
17
|
+
return self unless valid?
|
18
|
+
|
19
|
+
begin
|
20
|
+
doc = REXML::Document.new(@input)
|
21
|
+
@parsed_data = extract_data_from_xml(doc)
|
22
|
+
@parsed_data[:format] = 'xml'
|
23
|
+
@parsed_data[:type] = 'structured_xml'
|
24
|
+
rescue REXML::ParseException => e
|
25
|
+
@errors << "XML parsing error: #{e.message}"
|
26
|
+
end
|
27
|
+
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def valid?
|
32
|
+
return false unless @input.is_a?(String)
|
33
|
+
return false if @input.strip.empty?
|
34
|
+
|
35
|
+
begin
|
36
|
+
REXML::Document.new(@input)
|
37
|
+
true
|
38
|
+
rescue REXML::ParseException => e
|
39
|
+
@errors << "Invalid XML: #{e.message}"
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_hash
|
45
|
+
@parsed_data
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def extract_data_from_xml(doc)
|
51
|
+
data = {}
|
52
|
+
|
53
|
+
# Extract common prompt elements
|
54
|
+
if (prompt_element = doc.elements['prompt'])
|
55
|
+
data[:system] = prompt_element.elements['system']&.text
|
56
|
+
data[:instructions] = prompt_element.elements['instructions']&.text
|
57
|
+
data[:text] = data[:instructions] || prompt_element.text&.strip
|
58
|
+
|
59
|
+
# Extract context
|
60
|
+
if (context_element = prompt_element.elements['context'])
|
61
|
+
data[:context] = extract_hash_from_element(context_element)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Extract data section
|
65
|
+
if (data_element = prompt_element.elements['data'])
|
66
|
+
data[:data] = extract_hash_from_element(data_element)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Extract examples
|
70
|
+
if (examples_element = prompt_element.elements['examples'])
|
71
|
+
data[:examples] = extract_examples(examples_element)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Extract grounding
|
75
|
+
if (grounding_element = prompt_element.elements['grounding'])
|
76
|
+
data[:grounding] = extract_grounding(grounding_element)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Extract tools
|
80
|
+
if (tools_element = prompt_element.elements['tools'])
|
81
|
+
data[:tools] = extract_tools(tools_element)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
data
|
86
|
+
end
|
87
|
+
|
88
|
+
def extract_hash_from_element(element)
|
89
|
+
hash = {}
|
90
|
+
element.elements.each do |child|
|
91
|
+
hash[child.name.to_sym] = child.text
|
92
|
+
end
|
93
|
+
hash
|
94
|
+
end
|
95
|
+
|
96
|
+
def extract_examples(examples_element)
|
97
|
+
examples = []
|
98
|
+
examples_element.elements.each('example') do |example|
|
99
|
+
examples << if example.elements['input'] && example.elements['output']
|
100
|
+
{
|
101
|
+
input: example.elements['input'].text,
|
102
|
+
output: example.elements['output'].text
|
103
|
+
}
|
104
|
+
else
|
105
|
+
example.text
|
106
|
+
end
|
107
|
+
end
|
108
|
+
examples
|
109
|
+
end
|
110
|
+
|
111
|
+
def extract_grounding(grounding_element)
|
112
|
+
grounding = {}
|
113
|
+
grounding[:require_quotes] = grounding_element.elements['require_quotes']&.text == 'true'
|
114
|
+
grounding[:require_sources] = grounding_element.elements['require_sources']&.text == 'true'
|
115
|
+
grounding[:allow_uncertainty] = grounding_element.elements['allow_uncertainty']&.text == 'true'
|
116
|
+
grounding
|
117
|
+
end
|
118
|
+
|
119
|
+
def extract_tools(tools_element)
|
120
|
+
tools = []
|
121
|
+
tools_element.elements.each('tool') do |tool|
|
122
|
+
tool_data = { name: tool.attributes['name'] }
|
123
|
+
if (params_element = tool.elements['parameters'])
|
124
|
+
tool_data[:parameters] = extract_hash_from_element(params_element)
|
125
|
+
end
|
126
|
+
tools << tool_data
|
127
|
+
end
|
128
|
+
tools
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'psych'
|
4
|
+
|
5
|
+
module Artificial
|
6
|
+
module Parsers
|
7
|
+
class YAMLParser
|
8
|
+
attr_reader :input, :parsed_data, :errors
|
9
|
+
|
10
|
+
def initialize(input)
|
11
|
+
@input = input
|
12
|
+
@parsed_data = {}
|
13
|
+
@errors = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse
|
17
|
+
return self unless valid?
|
18
|
+
|
19
|
+
begin
|
20
|
+
yaml_data = Psych.safe_load(@input, permitted_classes: [Date, Time, DateTime, Symbol])
|
21
|
+
@parsed_data = normalize_yaml_data(yaml_data)
|
22
|
+
@parsed_data[:format] = 'yaml'
|
23
|
+
@parsed_data[:type] = 'structured_yaml'
|
24
|
+
rescue Psych::SyntaxError => e
|
25
|
+
@errors << "YAML parsing error: #{e.message}"
|
26
|
+
rescue StandardError => e
|
27
|
+
@errors << "YAML processing error: #{e.message}"
|
28
|
+
end
|
29
|
+
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def valid?
|
34
|
+
return false unless @input.is_a?(String)
|
35
|
+
return false if @input.strip.empty?
|
36
|
+
|
37
|
+
begin
|
38
|
+
Psych.safe_load(@input, permitted_classes: [Date, Time, DateTime, Symbol])
|
39
|
+
true
|
40
|
+
rescue Psych::SyntaxError => e
|
41
|
+
@errors << "Invalid YAML: #{e.message}"
|
42
|
+
false
|
43
|
+
rescue StandardError => e
|
44
|
+
@errors << "YAML validation error: #{e.message}"
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_hash
|
50
|
+
@parsed_data
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def normalize_yaml_data(yaml_data)
|
56
|
+
return {} unless yaml_data.is_a?(Hash)
|
57
|
+
|
58
|
+
# Convert string keys to symbols for consistency
|
59
|
+
normalized = {}
|
60
|
+
yaml_data.each do |key, value|
|
61
|
+
symbol_key = key.to_s.to_sym
|
62
|
+
normalized[symbol_key] = normalize_value(value)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Ensure common fields are present
|
66
|
+
normalized[:text] ||= normalized[:instructions]
|
67
|
+
normalized[:examples] ||= []
|
68
|
+
normalized[:context] ||= {}
|
69
|
+
normalized[:grounding] ||= {}
|
70
|
+
normalized[:tools] ||= []
|
71
|
+
|
72
|
+
normalized
|
73
|
+
end
|
74
|
+
|
75
|
+
def normalize_value(value)
|
76
|
+
case value
|
77
|
+
when Hash
|
78
|
+
value.transform_keys { |k| k.to_s.to_sym }
|
79
|
+
when Array
|
80
|
+
value.map { |v| normalize_value(v) }
|
81
|
+
else
|
82
|
+
value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,440 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rexml/document'
|
4
|
+
require 'json'
|
5
|
+
require 'psych'
|
6
|
+
|
7
|
+
module Artificial
|
8
|
+
class Prompt
|
9
|
+
attr_accessor :text, :system, :messages, :context, :examples, :thinking,
|
10
|
+
:assistant_prefill, :format, :grounding, :data, :instructions,
|
11
|
+
:constraints, :output_format, :tone, :tools, :retrieval,
|
12
|
+
:citation_style, :validation, :optimization, :documents
|
13
|
+
|
14
|
+
def initialize(input = nil, **options)
|
15
|
+
@format = options[:format] || 'xml'
|
16
|
+
@context = {}
|
17
|
+
@examples = []
|
18
|
+
@constraints = []
|
19
|
+
@grounding = {}
|
20
|
+
@tools = []
|
21
|
+
@documents = []
|
22
|
+
|
23
|
+
parse_input(input, options)
|
24
|
+
apply_options(options)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Generate the final prompt structure
|
28
|
+
def to_s
|
29
|
+
case @format
|
30
|
+
when 'xml'
|
31
|
+
generate_xml_prompt
|
32
|
+
when 'string'
|
33
|
+
generate_string_prompt
|
34
|
+
else
|
35
|
+
generate_xml_prompt
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Method chaining support
|
40
|
+
def with_system(system_prompt)
|
41
|
+
validate_system_prompt(system_prompt) if system_prompt
|
42
|
+
@system = system_prompt
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def with_context(**context_options)
|
47
|
+
@context.merge!(context_options)
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def with_examples(*example_list)
|
52
|
+
@examples.concat(example_list)
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def with_thinking(enabled: true, style: 'step_by_step', show_reasoning: true)
|
57
|
+
@thinking = { enabled: enabled, style: style, show_reasoning: show_reasoning }
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def with_grounding(require_quotes: false, require_sources: false, allow_uncertainty: false)
|
62
|
+
@grounding = {
|
63
|
+
require_quotes: require_quotes,
|
64
|
+
require_sources: require_sources,
|
65
|
+
allow_uncertainty: allow_uncertainty
|
66
|
+
}
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
def with_constraints(*constraint_list)
|
71
|
+
@constraints.concat(constraint_list)
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
def with_tools(*tool_list)
|
76
|
+
@tools.concat(tool_list)
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
def with_data(data_hash)
|
81
|
+
@data = data_hash
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def with_documents(*document_list)
|
86
|
+
@documents.concat(document_list)
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
def with_prefill(prefill_text)
|
91
|
+
@assistant_prefill = prefill_text
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def parse_input(input, options)
|
98
|
+
case input
|
99
|
+
when String
|
100
|
+
@text = input
|
101
|
+
when Hash
|
102
|
+
parse_hash_input(input)
|
103
|
+
when Array
|
104
|
+
parse_message_array(input)
|
105
|
+
when nil
|
106
|
+
# Handle options-only initialization
|
107
|
+
@text = options[:text] || options[:instructions]
|
108
|
+
else
|
109
|
+
raise ArgumentError, "Unsupported input type: #{input.class}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def parse_hash_input(hash)
|
114
|
+
@text = hash[:text] || hash[:instructions]
|
115
|
+
@system = hash[:system]
|
116
|
+
@messages = hash[:messages]
|
117
|
+
@context = hash[:context] || {}
|
118
|
+
@examples = hash[:examples] || []
|
119
|
+
@data = hash[:data]
|
120
|
+
@instructions = hash[:instructions]
|
121
|
+
|
122
|
+
# Handle YAML/JSON parsing if needed
|
123
|
+
return unless hash.key?(:yaml) || hash.key?(:json)
|
124
|
+
|
125
|
+
parse_structured_data(hash)
|
126
|
+
end
|
127
|
+
|
128
|
+
def parse_message_array(messages)
|
129
|
+
validate_messages(messages)
|
130
|
+
@messages = messages
|
131
|
+
end
|
132
|
+
|
133
|
+
def parse_structured_data(hash)
|
134
|
+
if hash[:yaml]
|
135
|
+
parsed = Psych.safe_load(hash[:yaml])
|
136
|
+
merge_parsed_data(parsed)
|
137
|
+
elsif hash[:json]
|
138
|
+
parsed = JSON.parse(hash[:json])
|
139
|
+
merge_parsed_data(parsed)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def merge_parsed_data(parsed)
|
144
|
+
@text ||= parsed['text'] || parsed['instructions']
|
145
|
+
@system ||= parsed['system']
|
146
|
+
@context = (@context || {}).merge(parsed['context'] || {})
|
147
|
+
@examples = (@examples || []).concat(parsed['examples'] || [])
|
148
|
+
end
|
149
|
+
|
150
|
+
def apply_options(options)
|
151
|
+
@system ||= options[:system]
|
152
|
+
validate_system_prompt(@system) if @system
|
153
|
+
|
154
|
+
@context = (@context || {}).merge(options[:context] || {})
|
155
|
+
@examples = (@examples || []).concat(options[:examples] || [])
|
156
|
+
@thinking = options[:thinking] if options[:thinking]
|
157
|
+
@assistant_prefill = options[:assistant_prefill] if options[:assistant_prefill]
|
158
|
+
@grounding = (@grounding || {}).merge(options[:grounding] || {})
|
159
|
+
@constraints = (@constraints || []).concat(options[:constraints] || [])
|
160
|
+
@output_format = options[:output_format] if options[:output_format]
|
161
|
+
@tone = options[:tone] if options[:tone]
|
162
|
+
@tools = (@tools || []).concat(options[:tools] || [])
|
163
|
+
@retrieval = options[:retrieval] if options[:retrieval]
|
164
|
+
@citation_style = options[:citation_style] if options[:citation_style]
|
165
|
+
@validation = options[:validation] if options[:validation]
|
166
|
+
@optimization = options[:optimization] if options[:optimization]
|
167
|
+
@documents = (@documents || []).concat(options[:documents] || [])
|
168
|
+
@data = options[:data] if options[:data]
|
169
|
+
end
|
170
|
+
|
171
|
+
def validate_messages(messages)
|
172
|
+
return unless messages.is_a?(Array)
|
173
|
+
|
174
|
+
validator = Artificial::Validators::MessageValidator.new(messages)
|
175
|
+
return if validator.valid?
|
176
|
+
|
177
|
+
raise ArgumentError, validator.error_messages.join('; ')
|
178
|
+
end
|
179
|
+
|
180
|
+
def validate_system_prompt(system_prompt)
|
181
|
+
return unless system_prompt && !system_prompt.strip.empty?
|
182
|
+
|
183
|
+
validator = Artificial::Validators::RoleValidator.new(system_prompt)
|
184
|
+
validator.validate
|
185
|
+
|
186
|
+
# Log suggestions but don't raise errors for effectiveness
|
187
|
+
if validator.optimization_suggestions.any?
|
188
|
+
# Could add logging here in the future
|
189
|
+
# puts "Role optimization suggestions: #{validator.optimization_suggestions.join('; ')}"
|
190
|
+
end
|
191
|
+
|
192
|
+
return if validator.valid?
|
193
|
+
|
194
|
+
raise ArgumentError, validator.error_messages.join('; ')
|
195
|
+
end
|
196
|
+
|
197
|
+
def generate_xml_prompt
|
198
|
+
doc = REXML::Document.new
|
199
|
+
root = doc.add_element('prompt')
|
200
|
+
|
201
|
+
# Handle message array format (conversation)
|
202
|
+
if @messages && !@messages.empty?
|
203
|
+
add_messages_section(root)
|
204
|
+
return output_xml(doc)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Add long context documents first for optimization
|
208
|
+
add_documents_section(root) if @documents && !@documents.empty?
|
209
|
+
|
210
|
+
# Add system instructions
|
211
|
+
if @system
|
212
|
+
system_element = root.add_element('system')
|
213
|
+
system_element.text = @system
|
214
|
+
end
|
215
|
+
|
216
|
+
# Add context information
|
217
|
+
add_context_section(root) if @context && !@context.empty?
|
218
|
+
|
219
|
+
# Add data section (separated from instructions)
|
220
|
+
add_data_section(root) if @data
|
221
|
+
|
222
|
+
# Add instructions
|
223
|
+
if @instructions || @text
|
224
|
+
instructions_element = root.add_element('instructions')
|
225
|
+
instructions_element.text = @instructions || @text
|
226
|
+
|
227
|
+
# Add constraints
|
228
|
+
if @constraints && !@constraints.empty?
|
229
|
+
constraints_element = instructions_element.add_element('constraints')
|
230
|
+
@constraints.each do |constraint|
|
231
|
+
constraint_element = constraints_element.add_element('constraint')
|
232
|
+
constraint_element.text = constraint
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Add examples
|
238
|
+
add_examples_section(root) if @examples && !@examples.empty?
|
239
|
+
|
240
|
+
# Add thinking instructions
|
241
|
+
add_thinking_section(root) if @thinking && @thinking[:enabled]
|
242
|
+
|
243
|
+
# Add grounding requirements
|
244
|
+
add_grounding_section(root) if @grounding && !@grounding.empty?
|
245
|
+
|
246
|
+
# Add tools
|
247
|
+
add_tools_section(root) if @tools && !@tools.empty?
|
248
|
+
|
249
|
+
# Add retrieval configuration
|
250
|
+
add_retrieval_section(root) if @retrieval
|
251
|
+
|
252
|
+
# Add output formatting
|
253
|
+
add_output_section(root) if @output_format || @tone
|
254
|
+
|
255
|
+
# Add assistant prefill
|
256
|
+
if @assistant_prefill
|
257
|
+
prefill_element = root.add_element('assistant_prefill')
|
258
|
+
prefill_element.text = @assistant_prefill
|
259
|
+
end
|
260
|
+
|
261
|
+
# Convert to string and clean up
|
262
|
+
output_xml(doc)
|
263
|
+
end
|
264
|
+
|
265
|
+
def generate_string_prompt
|
266
|
+
parts = []
|
267
|
+
|
268
|
+
# Add system prompt
|
269
|
+
parts << "System: #{@system}" if @system
|
270
|
+
|
271
|
+
# Add context
|
272
|
+
if @context && !@context.empty?
|
273
|
+
context_parts = @context.map { |k, v| "#{k}: #{v}" }
|
274
|
+
parts << "Context: #{context_parts.join(', ')}"
|
275
|
+
end
|
276
|
+
|
277
|
+
# Add main text/instructions
|
278
|
+
parts << (@instructions || @text) if @instructions || @text
|
279
|
+
|
280
|
+
# Add constraints
|
281
|
+
parts << "Constraints: #{@constraints.join('; ')}" if @constraints && !@constraints.empty?
|
282
|
+
|
283
|
+
# Add examples
|
284
|
+
parts << "Examples: #{@examples.map(&:to_s).join('; ')}" if @examples && !@examples.empty?
|
285
|
+
|
286
|
+
# Add assistant prefill
|
287
|
+
parts << "Assistant: #{@assistant_prefill}" if @assistant_prefill
|
288
|
+
|
289
|
+
parts.join("\n\n")
|
290
|
+
end
|
291
|
+
|
292
|
+
def output_xml(doc)
|
293
|
+
output = String.new
|
294
|
+
doc.write(output, 0)
|
295
|
+
output
|
296
|
+
end
|
297
|
+
|
298
|
+
def add_messages_section(root)
|
299
|
+
messages_element = root.add_element('messages')
|
300
|
+
@messages.each do |message|
|
301
|
+
message_element = messages_element.add_element('message')
|
302
|
+
message_element.add_attribute('role', message[:role] || message['role'])
|
303
|
+
message_element.text = message[:content] || message['content']
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def add_documents_section(root)
|
308
|
+
documents_element = root.add_element('documents')
|
309
|
+
@documents.each do |doc|
|
310
|
+
doc_element = documents_element.add_element('document')
|
311
|
+
if doc.is_a?(Hash)
|
312
|
+
doc_element.add_attribute('source', doc[:source]) if doc[:source]
|
313
|
+
doc_element.text = doc[:content]
|
314
|
+
if doc[:metadata]
|
315
|
+
metadata_element = doc_element.add_element('metadata')
|
316
|
+
doc[:metadata].each do |key, value|
|
317
|
+
meta_element = metadata_element.add_element(key.to_s)
|
318
|
+
meta_element.text = value.to_s
|
319
|
+
end
|
320
|
+
end
|
321
|
+
else
|
322
|
+
doc_element.text = doc.to_s
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def add_context_section(root)
|
328
|
+
context_element = root.add_element('context')
|
329
|
+
@context.each do |key, value|
|
330
|
+
element = context_element.add_element(key.to_s)
|
331
|
+
element.text = value.to_s
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def add_data_section(root)
|
336
|
+
data_element = root.add_element('data')
|
337
|
+
case @data
|
338
|
+
when Hash
|
339
|
+
@data.each do |key, value|
|
340
|
+
element = data_element.add_element(key.to_s)
|
341
|
+
element.text = value.to_s
|
342
|
+
end
|
343
|
+
else
|
344
|
+
data_element.text = @data.to_s
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def add_examples_section(root)
|
349
|
+
examples_element = root.add_element('examples')
|
350
|
+
@examples.each do |example|
|
351
|
+
example_element = examples_element.add_element('example')
|
352
|
+
if example.is_a?(Hash)
|
353
|
+
if example[:input]
|
354
|
+
input_element = example_element.add_element('input')
|
355
|
+
input_element.text = example[:input].to_s
|
356
|
+
end
|
357
|
+
if example[:output]
|
358
|
+
output_element = example_element.add_element('output')
|
359
|
+
output_element.text = example[:output].to_s
|
360
|
+
end
|
361
|
+
else
|
362
|
+
example_element.text = example.to_s
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def add_thinking_section(root)
|
368
|
+
thinking_element = root.add_element('thinking')
|
369
|
+
thinking_element.add_attribute('enabled', @thinking[:enabled].to_s)
|
370
|
+
thinking_element.add_attribute('style', @thinking[:style]) if @thinking[:style]
|
371
|
+
|
372
|
+
return unless @thinking[:show_reasoning]
|
373
|
+
|
374
|
+
instruction = 'Think through this step by step. Show your reasoning process.'
|
375
|
+
thinking_element.text = instruction
|
376
|
+
end
|
377
|
+
|
378
|
+
def add_grounding_section(root)
|
379
|
+
grounding_element = root.add_element('grounding')
|
380
|
+
|
381
|
+
if @grounding[:require_quotes]
|
382
|
+
require_quotes_element = grounding_element.add_element('require_quotes')
|
383
|
+
require_quotes_element.text = 'true'
|
384
|
+
end
|
385
|
+
|
386
|
+
if @grounding[:require_sources]
|
387
|
+
require_sources_element = grounding_element.add_element('require_sources')
|
388
|
+
require_sources_element.text = 'true'
|
389
|
+
end
|
390
|
+
|
391
|
+
return unless @grounding[:allow_uncertainty]
|
392
|
+
|
393
|
+
allow_uncertainty_element = grounding_element.add_element('allow_uncertainty')
|
394
|
+
allow_uncertainty_element.text = 'true'
|
395
|
+
end
|
396
|
+
|
397
|
+
def add_tools_section(root)
|
398
|
+
tools_element = root.add_element('tools')
|
399
|
+
@tools.each do |tool|
|
400
|
+
tool_element = tools_element.add_element('tool')
|
401
|
+
if tool.is_a?(Hash)
|
402
|
+
tool_element.add_attribute('name', tool[:name]) if tool[:name]
|
403
|
+
if tool[:parameters]
|
404
|
+
params_element = tool_element.add_element('parameters')
|
405
|
+
tool[:parameters].each do |key, value|
|
406
|
+
param_element = params_element.add_element(key.to_s)
|
407
|
+
param_element.text = value.to_s
|
408
|
+
end
|
409
|
+
end
|
410
|
+
else
|
411
|
+
tool_element.text = tool.to_s
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
def add_retrieval_section(root)
|
417
|
+
retrieval_element = root.add_element('retrieval')
|
418
|
+
|
419
|
+
if @retrieval[:sources]
|
420
|
+
sources_element = retrieval_element.add_element('sources')
|
421
|
+
@retrieval[:sources].each do |source|
|
422
|
+
source_element = sources_element.add_element('source')
|
423
|
+
source_element.text = source
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
retrieval_element.add_attribute('max_results', @retrieval[:max_results].to_s) if @retrieval[:max_results]
|
428
|
+
return unless @retrieval[:similarity_threshold]
|
429
|
+
|
430
|
+
retrieval_element.add_attribute('similarity_threshold',
|
431
|
+
@retrieval[:similarity_threshold].to_s)
|
432
|
+
end
|
433
|
+
|
434
|
+
def add_output_section(root)
|
435
|
+
output_element = root.add_element('output')
|
436
|
+
output_element.add_attribute('format', @output_format) if @output_format
|
437
|
+
output_element.add_attribute('tone', @tone) if @tone
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|