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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 20d89e77f87e37c150c0e2b3ebc0677f0a65e77c6a674aba37a9834d3986ba3b
|
4
|
+
data.tar.gz: baf31ac8198a8626ecac93a2719390c89155c49736321614b1375ca2889b46ea
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 36291dba209504dbdcca673c6dcc4e6393807fef54e5184b0576b7f5137cb6873d69d23e3cf5926c414c6a9756ed809798dd03b4b70806c320de968f78b0b6b3
|
7
|
+
data.tar.gz: 53432117f9d4ba5465bc8b9a35f6c7c0a56e0e050d42a112751fc51dcfc47ddd94e9655998c02b140e4031466a9450a58c46dbd01e686f8e82f801160cc32427
|
data/README.md
ADDED
@@ -0,0 +1,303 @@
|
|
1
|
+
# Artificial
|
2
|
+
|
3
|
+
A comprehensive Ruby gem for prompt engineering and management. Build, optimize, and maintain AI prompts with advanced features like role-based prompting, data separation, multi-format input support, and structured XML output.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- **Multi-format Input Support**: String, XML, YAML, JSON, and message arrays
|
8
|
+
- **Role-based Prompting**: Expert role assignment for optimal AI performance
|
9
|
+
- **Data Separation**: Clear separation between instructions and data
|
10
|
+
- **XML Structure**: Structured prompts with proper organization
|
11
|
+
- **Method Chaining**: Fluent API for building complex prompts
|
12
|
+
- **Validation**: Built-in validation for messages and roles
|
13
|
+
- **Hallucination Prevention**: Grounding requirements and source attribution
|
14
|
+
- **Tool Integration**: Support for function calling and external tools
|
15
|
+
- **Long Context Optimization**: Efficient handling of large documents
|
16
|
+
- **Example Management**: Structured examples with input/output pairs
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Install the gem and add to the application's Gemfile by executing:
|
21
|
+
|
22
|
+
$ bundle add artificial
|
23
|
+
|
24
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
25
|
+
|
26
|
+
$ gem install artificial
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
### Basic Usage
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
require 'artificial'
|
34
|
+
|
35
|
+
# Simple prompt creation
|
36
|
+
prompt = Artificial::Prompt.new("What is Ruby?")
|
37
|
+
puts prompt.to_s
|
38
|
+
```
|
39
|
+
|
40
|
+
### Role-based Prompting
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
# Expert role assignment
|
44
|
+
prompt = Artificial::Prompt.new(
|
45
|
+
text: "Explain object-oriented programming",
|
46
|
+
system: "You are a Ruby programming expert with 10 years of experience"
|
47
|
+
)
|
48
|
+
puts prompt.to_s
|
49
|
+
```
|
50
|
+
|
51
|
+
### Method Chaining
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
# Fluent API for building complex prompts
|
55
|
+
prompt = Artificial::Prompt.new("Analyze this code")
|
56
|
+
.with_system("You are a senior Ruby developer")
|
57
|
+
.with_context(audience: "junior developers", goal: "code review")
|
58
|
+
.with_examples({ input: "class MyClass; end", output: "Simple class definition" })
|
59
|
+
.with_thinking(enabled: true)
|
60
|
+
.with_grounding(require_quotes: true, require_sources: true)
|
61
|
+
|
62
|
+
puts prompt.to_s
|
63
|
+
```
|
64
|
+
|
65
|
+
### Data Separation
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
# Clear separation between instructions and data
|
69
|
+
prompt = Artificial::Prompt.new(
|
70
|
+
instructions: "Analyze the sales data and identify trends",
|
71
|
+
data: {
|
72
|
+
q1_sales: "100000",
|
73
|
+
q2_sales: "120000",
|
74
|
+
q3_sales: "110000",
|
75
|
+
q4_sales: "130000"
|
76
|
+
}
|
77
|
+
)
|
78
|
+
puts prompt.to_s
|
79
|
+
```
|
80
|
+
|
81
|
+
### Tool Integration
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
# Function calling support
|
85
|
+
prompt = Artificial::Prompt.new("Calculate financial metrics")
|
86
|
+
.with_tools(
|
87
|
+
{ name: "calculate_ratios", parameters: { data: "quarterly_reports" } },
|
88
|
+
{ name: "generate_chart", parameters: { chart_type: "trend" } }
|
89
|
+
)
|
90
|
+
puts prompt.to_s
|
91
|
+
```
|
92
|
+
|
93
|
+
### Message Arrays (Conversation Format)
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
# Multi-turn conversations
|
97
|
+
messages = [
|
98
|
+
{ role: "system", content: "You are a helpful Ruby programming assistant" },
|
99
|
+
{ role: "user", content: "How do I create a class in Ruby?" },
|
100
|
+
{ role: "assistant", content: "You can create a class using the 'class' keyword" },
|
101
|
+
{ role: "user", content: "Can you show me an example?" }
|
102
|
+
]
|
103
|
+
|
104
|
+
prompt = Artificial::Prompt.new(messages)
|
105
|
+
puts prompt.to_s
|
106
|
+
```
|
107
|
+
|
108
|
+
### Multi-format Input Parsing
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
# YAML format
|
112
|
+
yaml_input = <<~YAML
|
113
|
+
text: "Explain Ruby blocks"
|
114
|
+
system: "You are a Ruby expert"
|
115
|
+
context:
|
116
|
+
audience: "beginners"
|
117
|
+
goal: "education"
|
118
|
+
YAML
|
119
|
+
|
120
|
+
parsed = Artificial.parse(yaml_input)
|
121
|
+
puts parsed.to_hash[:text] # "Explain Ruby blocks"
|
122
|
+
|
123
|
+
# JSON format
|
124
|
+
json_input = '{"text": "What are Ruby gems?", "system": "You are a Ruby expert"}'
|
125
|
+
parsed = Artificial.parse(json_input)
|
126
|
+
puts parsed.to_hash[:text] # "What are Ruby gems?"
|
127
|
+
|
128
|
+
# Auto-detection
|
129
|
+
parsed = Artificial.parse("Simple text prompt")
|
130
|
+
puts parsed.to_hash[:format] # "string"
|
131
|
+
```
|
132
|
+
|
133
|
+
### Validation
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
# Message validation
|
137
|
+
messages = [
|
138
|
+
{ role: "system", content: "You are helpful" },
|
139
|
+
{ role: "user", content: "Hello" }
|
140
|
+
]
|
141
|
+
|
142
|
+
validator = Artificial.validate_messages(messages)
|
143
|
+
puts validator.valid? # true
|
144
|
+
|
145
|
+
# Role validation with optimization suggestions
|
146
|
+
validator = Artificial.validate_role("You are a Ruby programming expert")
|
147
|
+
puts validator.valid? # true
|
148
|
+
puts validator.effective? # true
|
149
|
+
puts validator.optimization_suggestions.join("; ")
|
150
|
+
```
|
151
|
+
|
152
|
+
### Long Context Documents
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
# Efficient handling of large documents
|
156
|
+
prompt = Artificial::Prompt.new("Summarize the key findings")
|
157
|
+
.with_documents(
|
158
|
+
{
|
159
|
+
content: "Long research document content...",
|
160
|
+
source: "research_report.pdf",
|
161
|
+
metadata: { author: "Dr. Smith", date: "2024-01-15" }
|
162
|
+
}
|
163
|
+
)
|
164
|
+
puts prompt.to_s
|
165
|
+
```
|
166
|
+
|
167
|
+
### Advanced Features
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
# Comprehensive prompt with all features
|
171
|
+
prompt = Artificial::Prompt.new(
|
172
|
+
text: "Analyze this code",
|
173
|
+
system: "You are a senior Ruby developer",
|
174
|
+
context: {
|
175
|
+
audience: "junior developers",
|
176
|
+
goal: "identify improvements",
|
177
|
+
workflow: "code_review"
|
178
|
+
},
|
179
|
+
examples: [
|
180
|
+
{ input: "bad_code.rb", output: "improvement_suggestions" }
|
181
|
+
],
|
182
|
+
thinking: { enabled: true, style: "step_by_step" },
|
183
|
+
grounding: {
|
184
|
+
require_quotes: true,
|
185
|
+
require_sources: true,
|
186
|
+
allow_uncertainty: true
|
187
|
+
},
|
188
|
+
constraints: [
|
189
|
+
"Quote specific code sections",
|
190
|
+
"Provide actionable suggestions",
|
191
|
+
"Consider performance implications"
|
192
|
+
]
|
193
|
+
)
|
194
|
+
|
195
|
+
puts prompt.to_s
|
196
|
+
```
|
197
|
+
|
198
|
+
## API Reference
|
199
|
+
|
200
|
+
### Artificial::Prompt
|
201
|
+
|
202
|
+
Main class for creating and managing prompts.
|
203
|
+
|
204
|
+
#### Methods
|
205
|
+
|
206
|
+
- `new(input = nil, **options)` - Create a new prompt
|
207
|
+
- `to_s` - Generate the final prompt string
|
208
|
+
- `with_system(system_prompt)` - Set system prompt with role validation
|
209
|
+
- `with_context(**context_options)` - Add contextual information
|
210
|
+
- `with_examples(*examples)` - Add structured examples
|
211
|
+
- `with_thinking(enabled: true, style: 'step_by_step')` - Enable step-by-step reasoning
|
212
|
+
- `with_grounding(**options)` - Add hallucination prevention
|
213
|
+
- `with_constraints(*constraints)` - Add explicit constraints
|
214
|
+
- `with_tools(*tools)` - Add function calling tools
|
215
|
+
- `with_documents(*documents)` - Add long context documents
|
216
|
+
- `with_data(data_hash)` - Add data separate from instructions
|
217
|
+
- `with_prefill(text)` - Set assistant response prefill
|
218
|
+
|
219
|
+
### Artificial Module Methods
|
220
|
+
|
221
|
+
- `Artificial.create_prompt(input, **options)` - Factory method for creating prompts
|
222
|
+
- `Artificial.parse(input, format: :auto)` - Parse input in various formats
|
223
|
+
- `Artificial.validate_messages(messages)` - Validate message array structure
|
224
|
+
- `Artificial.validate_role(system_prompt)` - Validate and optimize role prompts
|
225
|
+
|
226
|
+
## Advanced Usage
|
227
|
+
|
228
|
+
### Custom Parsers
|
229
|
+
|
230
|
+
The gem supports multiple input formats with automatic detection:
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
# Explicit format specification
|
234
|
+
xml_parsed = Artificial.parse(xml_string, format: :xml)
|
235
|
+
yaml_parsed = Artificial.parse(yaml_string, format: :yaml)
|
236
|
+
json_parsed = Artificial.parse(json_string, format: :json)
|
237
|
+
|
238
|
+
# Auto-detection (recommended)
|
239
|
+
auto_parsed = Artificial.parse(input_string) # Automatically detects format
|
240
|
+
```
|
241
|
+
|
242
|
+
### Validation and Optimization
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
# Role optimization suggestions
|
246
|
+
validator = Artificial.validate_role("You are helpful")
|
247
|
+
puts validator.effective? # false - generic role
|
248
|
+
puts validator.optimization_suggestions
|
249
|
+
# => ["Generic roles may reduce AI performance. Consider specifying domain expertise"]
|
250
|
+
|
251
|
+
# Effective role
|
252
|
+
validator = Artificial.validate_role("You are a Ruby programming expert with 10 years of experience")
|
253
|
+
puts validator.effective? # true
|
254
|
+
puts validator.optimization_suggestions
|
255
|
+
# => ["Excellent! Domain expert roles typically improve response quality by 20-30%"]
|
256
|
+
```
|
257
|
+
|
258
|
+
### Error Handling
|
259
|
+
|
260
|
+
```ruby
|
261
|
+
begin
|
262
|
+
# Invalid message structure
|
263
|
+
messages = [{ role: "invalid", content: "test" }]
|
264
|
+
Artificial::Prompt.new(messages)
|
265
|
+
rescue ArgumentError => e
|
266
|
+
puts e.message # "Message at index 0 has invalid role 'invalid'"
|
267
|
+
end
|
268
|
+
```
|
269
|
+
|
270
|
+
## Examples
|
271
|
+
|
272
|
+
See the `examples/demo.rb` file for a comprehensive demonstration of all features.
|
273
|
+
|
274
|
+
## Development
|
275
|
+
|
276
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
277
|
+
|
278
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
279
|
+
|
280
|
+
## Contributing
|
281
|
+
|
282
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/roboruby/artificial.
|
283
|
+
|
284
|
+
## License
|
285
|
+
|
286
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
287
|
+
|
288
|
+
## Performance Benefits
|
289
|
+
|
290
|
+
Based on industry best practices from Anthropic's research, this gem provides:
|
291
|
+
|
292
|
+
- **Reduced hallucinations** through grounding requirements and source attribution
|
293
|
+
- **Better context handling** for long documents (20K+ tokens)
|
294
|
+
- **Structured output** that's easier to parse and validate
|
295
|
+
- **Optimized prompts** that follow proven patterns for maximum effectiveness
|
296
|
+
|
297
|
+
## Alignment with Industry Standards
|
298
|
+
|
299
|
+
This gem implements prompt engineering techniques aligned with:
|
300
|
+
|
301
|
+
- Anthropic's Interactive Prompt Engineering Tutorial
|
302
|
+
- OpenAI's best practices for function calling
|
303
|
+
- Production-ready hallucination prevention strategies
|
data/Rakefile
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
# Default task runs tests
|
7
|
+
task default: [:test]
|
8
|
+
|
9
|
+
# Main test task - runs all tests
|
10
|
+
Rake::TestTask.new(:test) do |t|
|
11
|
+
t.libs << 'test'
|
12
|
+
t.libs << 'lib'
|
13
|
+
t.test_files = FileList['test/**/test_*.rb']
|
14
|
+
t.verbose = true
|
15
|
+
end
|
16
|
+
|
17
|
+
# Individual test tasks for specific test files
|
18
|
+
namespace :test do
|
19
|
+
desc 'Run all tests'
|
20
|
+
Rake::TestTask.new(:all) do |t|
|
21
|
+
t.libs << 'test'
|
22
|
+
t.libs << 'lib'
|
23
|
+
t.test_files = FileList['test/**/test_*.rb']
|
24
|
+
t.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Run core prompt tests'
|
28
|
+
Rake::TestTask.new(:prompt) do |t|
|
29
|
+
t.libs << 'test'
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.test_files = FileList['test/test_artificial.rb']
|
32
|
+
t.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'Run parser and validator tests'
|
36
|
+
Rake::TestTask.new(:parsers) do |t|
|
37
|
+
t.libs << 'test'
|
38
|
+
t.libs << 'lib'
|
39
|
+
t.test_files = FileList['test/test_parsers.rb']
|
40
|
+
t.verbose = true
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'Run tests with verbose output'
|
44
|
+
Rake::TestTask.new(:verbose) do |t|
|
45
|
+
t.libs << 'test'
|
46
|
+
t.libs << 'lib'
|
47
|
+
t.test_files = FileList['test/**/test_*.rb']
|
48
|
+
t.verbose = true
|
49
|
+
t.options = '--verbose'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Demo task to run the examples
|
54
|
+
desc 'Run the demo script'
|
55
|
+
task :demo do
|
56
|
+
ruby 'examples/demo.rb'
|
57
|
+
end
|
58
|
+
|
59
|
+
# Code quality tasks
|
60
|
+
desc 'Run RuboCop'
|
61
|
+
task :rubocop do
|
62
|
+
sh 'rubocop'
|
63
|
+
end
|
64
|
+
|
65
|
+
desc 'Run all quality checks'
|
66
|
+
task quality: %i[test rubocop]
|
67
|
+
|
68
|
+
# Help task
|
69
|
+
desc 'Show available tasks'
|
70
|
+
task :help do
|
71
|
+
puts <<~HELP
|
72
|
+
Available Rake tasks:
|
73
|
+
|
74
|
+
Main tasks:
|
75
|
+
rake # Run all tests (default)
|
76
|
+
rake test # Run all tests
|
77
|
+
rake demo # Run the demo script
|
78
|
+
|
79
|
+
Test tasks:
|
80
|
+
rake test:all # Run all tests
|
81
|
+
rake test:prompt # Run core prompt tests only
|
82
|
+
rake test:parsers # Run parser and validator tests only
|
83
|
+
rake test:verbose # Run tests with verbose output
|
84
|
+
|
85
|
+
Quality tasks:
|
86
|
+
rake rubocop # Run RuboCop code analysis
|
87
|
+
rake quality # Run tests and RuboCop
|
88
|
+
|
89
|
+
Other tasks:
|
90
|
+
rake build # Build gem
|
91
|
+
rake install # Install gem locally
|
92
|
+
rake release # Release gem (maintainers only)
|
93
|
+
rake help # Show this help message
|
94
|
+
|
95
|
+
Examples:
|
96
|
+
rake # Quick test run
|
97
|
+
rake test:prompt # Test just the core functionality
|
98
|
+
rake demo # See the gem in action
|
99
|
+
rake quality # Full quality check before committing
|
100
|
+
HELP
|
101
|
+
end
|
data/examples/demo.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Add lib to the load path
|
5
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
6
|
+
|
7
|
+
require 'artificial'
|
8
|
+
|
9
|
+
puts '=== Artificial Ruby Gem Demo ==='
|
10
|
+
puts "Version: #{Artificial::VERSION}"
|
11
|
+
puts
|
12
|
+
|
13
|
+
# Example 1: Basic prompt creation
|
14
|
+
puts '1. Basic Prompt Creation:'
|
15
|
+
prompt = Artificial::Prompt.new('What is Ruby?')
|
16
|
+
puts prompt
|
17
|
+
puts
|
18
|
+
|
19
|
+
# Example 2: Role-based prompting
|
20
|
+
puts '2. Role-Based Prompting:'
|
21
|
+
prompt = Artificial::Prompt.new(
|
22
|
+
text: 'Explain object-oriented programming',
|
23
|
+
system: 'You are a Ruby programming expert with 10 years of experience'
|
24
|
+
)
|
25
|
+
puts prompt
|
26
|
+
puts
|
27
|
+
|
28
|
+
# Example 3: Method chaining
|
29
|
+
puts '3. Method Chaining:'
|
30
|
+
prompt = Artificial::Prompt.new('Analyze this code')
|
31
|
+
.with_system('You are a senior Ruby developer')
|
32
|
+
.with_context(audience: 'junior developers', goal: 'code review')
|
33
|
+
.with_examples({ input: 'class MyClass; end', output: 'Simple class definition' })
|
34
|
+
.with_thinking(enabled: true)
|
35
|
+
.with_grounding(require_quotes: true, require_sources: true)
|
36
|
+
|
37
|
+
puts prompt
|
38
|
+
puts
|
39
|
+
|
40
|
+
# Example 4: Data separation
|
41
|
+
puts '4. Data Separation:'
|
42
|
+
prompt = Artificial::Prompt.new(
|
43
|
+
instructions: 'Analyze the sales data and identify trends',
|
44
|
+
data: {
|
45
|
+
q1_sales: '100000',
|
46
|
+
q2_sales: '120000',
|
47
|
+
q3_sales: '110000',
|
48
|
+
q4_sales: '130000'
|
49
|
+
}
|
50
|
+
)
|
51
|
+
puts prompt
|
52
|
+
puts
|
53
|
+
|
54
|
+
# Example 5: Tool integration
|
55
|
+
puts '5. Tool Integration:'
|
56
|
+
prompt = Artificial::Prompt.new('Calculate financial metrics')
|
57
|
+
.with_tools(
|
58
|
+
{ name: 'calculate_ratios', parameters: { data: 'quarterly_reports' } },
|
59
|
+
{ name: 'generate_chart', parameters: { chart_type: 'trend' } }
|
60
|
+
)
|
61
|
+
puts prompt
|
62
|
+
puts
|
63
|
+
|
64
|
+
# Example 6: Long context documents
|
65
|
+
puts '6. Long Context Documents:'
|
66
|
+
prompt = Artificial::Prompt.new('Summarize the key findings')
|
67
|
+
.with_documents(
|
68
|
+
{
|
69
|
+
content: 'This is a sample research document with important findings...',
|
70
|
+
source: 'research_report.pdf',
|
71
|
+
metadata: { author: 'Dr. Smith', date: '2024-01-15' }
|
72
|
+
}
|
73
|
+
)
|
74
|
+
puts prompt
|
75
|
+
puts
|
76
|
+
|
77
|
+
# Example 7: Message array (conversation format)
|
78
|
+
puts '7. Message Array (Conversation Format):'
|
79
|
+
messages = [
|
80
|
+
{ role: 'system', content: 'You are a helpful Ruby programming assistant' },
|
81
|
+
{ role: 'user', content: 'How do I create a class in Ruby?' },
|
82
|
+
{ role: 'assistant', content: "You can create a class using the 'class' keyword" },
|
83
|
+
{ role: 'user', content: 'Can you show me an example?' }
|
84
|
+
]
|
85
|
+
prompt = Artificial::Prompt.new(messages)
|
86
|
+
puts prompt
|
87
|
+
puts
|
88
|
+
|
89
|
+
# Example 8: Auto-parsing different formats
|
90
|
+
puts '8. Auto-Parsing Different Formats:'
|
91
|
+
|
92
|
+
# YAML format
|
93
|
+
yaml_input = <<~YAML
|
94
|
+
text: "Explain Ruby blocks"
|
95
|
+
system: "You are a Ruby expert"
|
96
|
+
context:
|
97
|
+
audience: "beginners"
|
98
|
+
goal: "education"
|
99
|
+
YAML
|
100
|
+
|
101
|
+
parsed_yaml = Artificial.parse(yaml_input)
|
102
|
+
puts "YAML parsed as: #{parsed_yaml.to_hash[:format]}"
|
103
|
+
puts "Text: #{parsed_yaml.to_hash[:text]}"
|
104
|
+
puts
|
105
|
+
|
106
|
+
# JSON format
|
107
|
+
json_input = '{"text": "What are Ruby gems?", "system": "You are a Ruby expert"}'
|
108
|
+
parsed_json = Artificial.parse(json_input)
|
109
|
+
puts "JSON parsed as: #{parsed_json.to_hash[:format]}"
|
110
|
+
puts "Text: #{parsed_json.to_hash[:text]}"
|
111
|
+
puts
|
112
|
+
|
113
|
+
# Example 9: Validation
|
114
|
+
puts '9. Validation:'
|
115
|
+
|
116
|
+
# Valid messages
|
117
|
+
valid_messages = [
|
118
|
+
{ role: 'system', content: 'You are helpful' },
|
119
|
+
{ role: 'user', content: 'Hello' }
|
120
|
+
]
|
121
|
+
validator = Artificial.validate_messages(valid_messages)
|
122
|
+
puts "Messages valid: #{validator.valid?}"
|
123
|
+
|
124
|
+
# Role validation
|
125
|
+
role_validator = Artificial.validate_role('You are a Ruby programming expert')
|
126
|
+
puts "Role valid: #{role_validator.valid?}"
|
127
|
+
puts "Role effective: #{role_validator.effective?}"
|
128
|
+
puts "Suggestions: #{role_validator.optimization_suggestions.join('; ')}"
|
129
|
+
puts
|
130
|
+
|
131
|
+
# Example 10: Factory method
|
132
|
+
puts '10. Factory Method:'
|
133
|
+
prompt = Artificial.create_prompt('Hello world', system: 'You are helpful')
|
134
|
+
puts prompt
|
135
|
+
puts
|
136
|
+
|
137
|
+
puts '=== Demo Complete ==='
|
138
|
+
puts 'For more examples, check the documentation at https://rubydoc.info/gems/artificial'
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Artificial
|
6
|
+
module Parsers
|
7
|
+
class JSONParser
|
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
|
+
json_data = JSON.parse(@input)
|
21
|
+
@parsed_data = normalize_json_data(json_data)
|
22
|
+
@parsed_data[:format] = 'json'
|
23
|
+
@parsed_data[:type] = 'structured_json'
|
24
|
+
rescue JSON::ParserError => e
|
25
|
+
@errors << "JSON parsing error: #{e.message}"
|
26
|
+
rescue StandardError => e
|
27
|
+
@errors << "JSON 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
|
+
JSON.parse(@input)
|
39
|
+
true
|
40
|
+
rescue JSON::ParserError => e
|
41
|
+
@errors << "Invalid JSON: #{e.message}"
|
42
|
+
false
|
43
|
+
rescue StandardError => e
|
44
|
+
@errors << "JSON 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_json_data(json_data)
|
56
|
+
return {} unless json_data.is_a?(Hash)
|
57
|
+
|
58
|
+
# Convert string keys to symbols for consistency
|
59
|
+
normalized = {}
|
60
|
+
json_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
|
+
# Handle special JSON structures
|
73
|
+
normalized[:messages] = normalize_messages(normalized[:messages]) if normalized[:messages].is_a?(Array)
|
74
|
+
|
75
|
+
normalized
|
76
|
+
end
|
77
|
+
|
78
|
+
def normalize_value(value)
|
79
|
+
case value
|
80
|
+
when Hash
|
81
|
+
value.transform_keys { |k| k.to_s.to_sym }
|
82
|
+
when Array
|
83
|
+
value.map { |v| normalize_value(v) }
|
84
|
+
else
|
85
|
+
value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def normalize_messages(messages)
|
90
|
+
messages.map do |message|
|
91
|
+
if message.is_a?(Hash)
|
92
|
+
{
|
93
|
+
role: message['role']&.to_s || message[:role]&.to_s,
|
94
|
+
content: message['content'] || message[:content]
|
95
|
+
}
|
96
|
+
else
|
97
|
+
message
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Artificial
|
4
|
+
module Parsers
|
5
|
+
class StringParser
|
6
|
+
attr_reader :input, :parsed_data
|
7
|
+
|
8
|
+
def initialize(input)
|
9
|
+
@input = input
|
10
|
+
@parsed_data = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse
|
14
|
+
@parsed_data = {
|
15
|
+
text: @input,
|
16
|
+
format: 'string',
|
17
|
+
type: 'simple_text'
|
18
|
+
}
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid?
|
23
|
+
@input.is_a?(String) && !@input.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_hash
|
27
|
+
@parsed_data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|