hyrum 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 +154 -0
- data/bin/console +11 -0
- data/bin/hyrum +10 -0
- data/bin/setup +8 -0
- data/lib/hyrum/formats/formatter.rb +21 -0
- data/lib/hyrum/formats/templates/java.erb +30 -0
- data/lib/hyrum/formats/templates/javascript.erb +19 -0
- data/lib/hyrum/formats/templates/json.erb +1 -0
- data/lib/hyrum/formats/templates/python.erb +18 -0
- data/lib/hyrum/formats/templates/ruby.erb +19 -0
- data/lib/hyrum/formats/templates/text.erb +6 -0
- data/lib/hyrum/generators/fake_generator.rb +51 -0
- data/lib/hyrum/generators/message_generator.rb +28 -0
- data/lib/hyrum/generators/openai_generator.rb +82 -0
- data/lib/hyrum/script_options.rb +112 -0
- data/lib/hyrum/version.rb +5 -0
- data/lib/hyrum.rb +23 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6af9ff6689127c74e41729fbd056dadf9a811ec19a74f14ccd6a61413d2b3c0b
|
4
|
+
data.tar.gz: 900a08d6f56ad8e8cdf70f60fcf5782859b78420c95c9afaf0c86f78815ab449
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 14adcecb5a38a2fe356e5c9eb27e5fc254d1ce45953bfe32d595ad909813a49ea5a641a99f9b64daae75f3322f09f0bfa8891d9885990f8b1159d7f6f40fbead
|
7
|
+
data.tar.gz: 19537c266190bcf175f8a340d8828fd354f974fbdb80639018e0d8862103a81bd792967b144afa4e2f5bc2cad78fd94e185bc30f7044cf8db62c1c004278cf55
|
data/README.md
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# Hyrum's Message Generator
|
2
|
+
|
3
|
+
✨ A multi-language code generator to cope with Hyrum's law ✨
|
4
|
+
|
5
|
+
Hyrum's message generator (hyrum) generates a method in the chosen language that
|
6
|
+
returns one of several variations of a provided message at random.
|
7
|
+
|
8
|
+

|
9
|
+

|
10
|
+
[](https://github.com/grymoire7/hyrum/blob/main/LICENSE.txt)
|
11
|
+
|
12
|
+
## Explanation
|
13
|
+
[Hyrum's law][hd] states that all observable behaviors of your system will be
|
14
|
+
depended on by somebody. Some nice examples can be found [here][he].
|
15
|
+
|
16
|
+
One example that seems a little more difficult to handle is the case where a
|
17
|
+
status/error message is being returned to the user. How can you vary the message
|
18
|
+
so the user doesn't become dependent on the exact wording of the message?
|
19
|
+
Also, we don't want to spend a lot of time writing variations of the same message.
|
20
|
+
|
21
|
+
This is the use case Hyrum tries to solve. It uses an AI service (openai,
|
22
|
+
ollama, etc.) to generate variations of a provided message. The generated
|
23
|
+
variations are also formatted in the language/format of your choice (ruby,
|
24
|
+
json, etc.).
|
25
|
+
|
26
|
+
## Example
|
27
|
+
|
28
|
+
```bash
|
29
|
+
hyrum --service openai --key e418 --format ruby \
|
30
|
+
--message "The server refuses the attempt to brew coffee with a teapot"
|
31
|
+
```
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# frozen_string_literal: true
|
35
|
+
|
36
|
+
module Messages
|
37
|
+
MESSAGES = {
|
38
|
+
e418: [
|
39
|
+
"Invalid Brewing Method",
|
40
|
+
"Teapot not designed for coffee brewing",
|
41
|
+
"Please use a suitable brewing device",
|
42
|
+
"Coffee and tea are two distinct beverages with different requirements",
|
43
|
+
"Check your equipment and try again"
|
44
|
+
]
|
45
|
+
}.freeze
|
46
|
+
|
47
|
+
def self.message(key)
|
48
|
+
MESSAGES[key].sample
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
if $PROGRAM_NAME == __FILE__
|
53
|
+
puts Messages.message(ARGV[0].to_sym)
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
## Usage
|
58
|
+
|
59
|
+
```
|
60
|
+
❯ hyrum --help # OR from the repo as `./exe/hyrum --help`
|
61
|
+
Usage: hyrum [options]
|
62
|
+
-v, --[no-]verbose Run verbosely
|
63
|
+
-f, --format FORMAT Output format. Supported formats are:
|
64
|
+
ruby, javascript, python, java, text, json
|
65
|
+
-m, --message MESSAGE Status message
|
66
|
+
-k, --key KEY Message key
|
67
|
+
-s, --service SERVICE AI service: one of openai, ollama, fake
|
68
|
+
-d, --model MODEL AI model: must be a valid model for the selected service
|
69
|
+
-h, --help Show this message
|
70
|
+
--version Show version
|
71
|
+
```
|
72
|
+
|
73
|
+
## Installation
|
74
|
+
NOTE: This gem is not yet available on rubygems.org. You can install it from the
|
75
|
+
repository with `bundle exec rake install`. In the future, you can...
|
76
|
+
|
77
|
+
Install the gem and add to the application's Gemfile by executing:
|
78
|
+
|
79
|
+
$ bundle add hyrum
|
80
|
+
|
81
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
82
|
+
|
83
|
+
$ gem install hyrum
|
84
|
+
|
85
|
+
## Run without configuration
|
86
|
+
Hyrum normally requires configuration to access an AI service provider. However,
|
87
|
+
if you want to see output quickly, you can use the `-s fake` option to use a fake
|
88
|
+
service provider that will generate stock responses.
|
89
|
+
|
90
|
+
```bash
|
91
|
+
hyrum -s fake -f ruby -m "anything here"
|
92
|
+
```
|
93
|
+
|
94
|
+
You don't even need to install the gem to use Hyrum, fake service provider or not.
|
95
|
+
You can run the executable directly from a cloned repository.
|
96
|
+
|
97
|
+
```bash
|
98
|
+
./exec/hyrum -s fake -f ruby -m "anything here"
|
99
|
+
```
|
100
|
+
|
101
|
+
## Configruation
|
102
|
+
|
103
|
+
### OpenAI (`--service openai`)
|
104
|
+
Hyrum requires an OpenAI API token to access the language models. The API token should be
|
105
|
+
set as an environment variable as shown below.
|
106
|
+
|
107
|
+
```bash
|
108
|
+
export OPENAI_ACCESS_TOKEN=your_open_ai_token
|
109
|
+
```
|
110
|
+
|
111
|
+
If you specify the `openai` service but no model, Hyrum will use the `gpt-o4-mini`.
|
112
|
+
|
113
|
+
### Ollama (`--service ollama`)
|
114
|
+
If you specify the `ollama` service, Hyrum will attempt to use the Ollama API
|
115
|
+
running at `http://localhost:11434`. You can set the `OLLAMA_URL` environment
|
116
|
+
variable to specify a different URL.
|
117
|
+
|
118
|
+
Make sure your ollama server is running before using the `ollama` service.
|
119
|
+
|
120
|
+
```bash
|
121
|
+
ollama serve
|
122
|
+
```
|
123
|
+
|
124
|
+
Use `ollama list` to see the available models. For more information on using
|
125
|
+
ollama and downloading models, see the [ollama repository](http://ollama.com).
|
126
|
+
|
127
|
+
## Supported formats and AI services
|
128
|
+
|
129
|
+
See [Usage](#usage) for a list of supported formats and AI services.
|
130
|
+
|
131
|
+
## Compatibility
|
132
|
+
|
133
|
+
This gem is compatible with Ruby 3.1 or greater.
|
134
|
+
|
135
|
+
## Development
|
136
|
+
|
137
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
138
|
+
`rake spec` to run the tests.
|
139
|
+
|
140
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To
|
141
|
+
release a new version, update the version number in `version.rb`, and then run
|
142
|
+
`bundle exec rake release`, which will create a git tag for the version, push
|
143
|
+
git commits and the created tag, and push the `.gem` file to
|
144
|
+
[rubygems.org](https://rubygems.org).
|
145
|
+
|
146
|
+
## Contributing
|
147
|
+
|
148
|
+
Bug reports and pull requests are welcome on GitHub at
|
149
|
+
https://github.com/grymoire7/hyrum. This project is intended to be a safe,
|
150
|
+
welcoming space for collaboration, and contributors are expected to adhere to the
|
151
|
+
[code of conduct](https://github.com/grymoire7/hyrum/blob/main/CODE_OF_CONDUCT.md).
|
152
|
+
|
153
|
+
[hd]: https://www.laws-of-software.com/laws/hyrum/
|
154
|
+
[he]: https://abenezer.org/blog/hyrum-law-in-golang
|
data/bin/console
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'hyrum'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
require 'irb'
|
11
|
+
IRB.start(__FILE__)
|
data/bin/hyrum
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hyrum
|
4
|
+
module Formats
|
5
|
+
FORMATS = %i[ruby javascript python java text json].freeze
|
6
|
+
|
7
|
+
class Formatter
|
8
|
+
attr_reader :options
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def format(messages)
|
15
|
+
template_file = File.join(__dir__, 'templates', "#{options[:format]}.erb")
|
16
|
+
template = ERB.new(File.read(template_file), trim_mode: '-')
|
17
|
+
template.result_with_hash(messages: messages)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import java.util.HashMap;
|
2
|
+
import java.util.List;
|
3
|
+
import java.util.Random;
|
4
|
+
|
5
|
+
public class Messages {
|
6
|
+
private static final HashMap<String, List<String>> MESSAGES = new HashMap<>();
|
7
|
+
|
8
|
+
static {
|
9
|
+
<% messages.each do |key, values| -%>
|
10
|
+
MESSAGES.put("<%= key %>", List.of(
|
11
|
+
<%= values.map { |message| "\"#{message}\"" }.join(",\n ") %>
|
12
|
+
));
|
13
|
+
<% end -%>
|
14
|
+
}
|
15
|
+
|
16
|
+
public static String message(String key) {
|
17
|
+
List<String> messages = MESSAGES.get(key);
|
18
|
+
if (messages != null && !messages.isEmpty()) {
|
19
|
+
Random random = new Random();
|
20
|
+
return messages.get(random.nextInt(messages.size()));
|
21
|
+
}
|
22
|
+
return null;
|
23
|
+
}
|
24
|
+
|
25
|
+
public static void main(String[] args) {
|
26
|
+
if (args.length > 0) {
|
27
|
+
System.out.println(message(args[0]));
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
const Messages = {
|
2
|
+
MESSAGES: {
|
3
|
+
<% messages.each do |key, values| -%>
|
4
|
+
<%= key %>: [
|
5
|
+
<%= values.map { |message| "\"#{message}\"" }.join(",\n ") %>
|
6
|
+
]<%= "," unless key == messages.keys.last %>
|
7
|
+
<% end -%>
|
8
|
+
},
|
9
|
+
|
10
|
+
message(key) {
|
11
|
+
const messages = this.MESSAGES[key];
|
12
|
+
return messages[Math.floor(Math.random() * messages.length)];
|
13
|
+
}
|
14
|
+
};
|
15
|
+
|
16
|
+
if (require.main === module) {
|
17
|
+
const key = process.argv[2];
|
18
|
+
console.log(Messages.message(key));
|
19
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= JSON.pretty_generate(messages) %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import random
|
2
|
+
import sys
|
3
|
+
|
4
|
+
class Messages:
|
5
|
+
MESSAGES = {
|
6
|
+
<% messages.each do |key, values| -%>
|
7
|
+
'<%= key %>': [
|
8
|
+
<%= values.map { |message| "\"#{message}\"" }.join(",\n ") %>
|
9
|
+
]<%= "," unless key == messages.keys.last %>
|
10
|
+
<% end -%>
|
11
|
+
}
|
12
|
+
|
13
|
+
@staticmethod
|
14
|
+
def message(key):
|
15
|
+
return random.choice(Messages.MESSAGES[key])
|
16
|
+
|
17
|
+
if __name__ == "__main__":
|
18
|
+
print(Messages.message(sys.argv[1]))
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Messages
|
4
|
+
MESSAGES = {
|
5
|
+
<% messages.each do |key, values| -%>
|
6
|
+
<%= key %>: [
|
7
|
+
<%= values.map { |message| "\"#{message}\"" }.join(",\n ") %>
|
8
|
+
]<%= "," unless key == messages.keys.last %>
|
9
|
+
<% end -%>
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
def self.message(key)
|
13
|
+
MESSAGES[key].sample
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if $PROGRAM_NAME == __FILE__
|
18
|
+
puts Messages.message(ARGV[0].to_sym)
|
19
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hyrum
|
4
|
+
module Generators
|
5
|
+
class FakeGenerator
|
6
|
+
FAKE_MESSAGES = %(
|
7
|
+
{
|
8
|
+
"e404": [
|
9
|
+
"We couldn't locate the resource you were looking for.",
|
10
|
+
"The resource you requested is not available at this time.",
|
11
|
+
"Unfortunately, we were unable to find the specified resource.",
|
12
|
+
"It seems the resource you're searching for does not exist.",
|
13
|
+
"The item you are trying to access is currently missing."
|
14
|
+
],
|
15
|
+
"e418": [
|
16
|
+
"I'm a teapot",
|
17
|
+
"The server refuses the attempt to brew coffee with a teapot",
|
18
|
+
"Coffee brewing denied: a teapot is not suitable for this operation.",
|
19
|
+
"Request failed: the server cannot process coffee with a teapot.",
|
20
|
+
"Brewing error: teapots are incompatible with coffee preparation.",
|
21
|
+
"Action halted: using a teapot to brew coffee is not permitted.",
|
22
|
+
"Invalid request: please use a coffee maker instead of a teapot."
|
23
|
+
],
|
24
|
+
"e500": [
|
25
|
+
"Internal Server Error",
|
26
|
+
"An unexpected condition was encountered"
|
27
|
+
],
|
28
|
+
"e503": [
|
29
|
+
"Service Unavailable",
|
30
|
+
"The server is currently unavailable"
|
31
|
+
],
|
32
|
+
"e504": [
|
33
|
+
"Gateway Timeout",
|
34
|
+
"The server is currently unavailable"
|
35
|
+
]
|
36
|
+
}
|
37
|
+
)
|
38
|
+
|
39
|
+
attr_reader :options
|
40
|
+
|
41
|
+
def initialize(options)
|
42
|
+
@options = options
|
43
|
+
@ai_service = options[:ai_service]
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate
|
47
|
+
JSON.parse(FAKE_MESSAGES)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hyrum
|
4
|
+
module Generators
|
5
|
+
AI_SERVICES = %i[openai ollama fake].freeze
|
6
|
+
|
7
|
+
AI_MODEL_DEFAULTS = {
|
8
|
+
openai: :'gpt-4o-mini',
|
9
|
+
ollama: :llama3,
|
10
|
+
fake: :fake
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
GENERATOR_CLASSES = {
|
14
|
+
openai: OpenaiGenerator,
|
15
|
+
ollama: OpenaiGenerator,
|
16
|
+
fake: FakeGenerator
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
class MessageGenerator
|
20
|
+
def self.create(options)
|
21
|
+
generator_class = GENERATOR_CLASSES[options[:ai_service].to_sym]
|
22
|
+
|
23
|
+
# Add error handling for invalid format
|
24
|
+
generator_class.new(options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openai'
|
4
|
+
require 'json'
|
5
|
+
require 'erb'
|
6
|
+
|
7
|
+
module Hyrum
|
8
|
+
module Generators
|
9
|
+
class OpenaiGenerator
|
10
|
+
attr_reader :options
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
@options = options
|
14
|
+
configure
|
15
|
+
@client = OpenAI::Client.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate
|
19
|
+
if options[:ai_model] == :fake
|
20
|
+
return JSON.parse(canned_content)
|
21
|
+
end
|
22
|
+
|
23
|
+
response = get_response(prompt)
|
24
|
+
puts "Response: #{JSON.pretty_generate(response)}" if options[:verbose]
|
25
|
+
content = response.dig('choices', 0, 'message', 'content')
|
26
|
+
JSON.parse(content)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def prompt
|
32
|
+
prompt = <<~PROMPT
|
33
|
+
Please provide 5 alternative status messages for the following message:
|
34
|
+
`<%= message %>`. The messages should be unique and informative. The messages
|
35
|
+
should be returned as json in the format: `{ "<%= key %>": ['list', 'of', 'messages']}`
|
36
|
+
The key should be `"<%= key %>"` followed by the list of messages.
|
37
|
+
PROMPT
|
38
|
+
erb_hash = { key: options[:key], message: options[:message] }
|
39
|
+
template = ERB.new(prompt, trim_mode: '-')
|
40
|
+
template.result_with_hash(erb_hash)
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_response(prompt)
|
44
|
+
@client.chat(
|
45
|
+
parameters: {
|
46
|
+
model: options[:ai_model],
|
47
|
+
response_format: { type: 'json_object' },
|
48
|
+
messages: [{ role: 'user', content: prompt}],
|
49
|
+
temperature: 0.7
|
50
|
+
}
|
51
|
+
)
|
52
|
+
rescue OpenAI::Error => e
|
53
|
+
puts "OpenAI::Error: #{e.message}"
|
54
|
+
exit
|
55
|
+
rescue Faraday::Error => e
|
56
|
+
puts "Faraday::Error: #{e.message}"
|
57
|
+
puts "Please check that the #{options[:ai_model]} model is valid."
|
58
|
+
exit
|
59
|
+
end
|
60
|
+
|
61
|
+
def configure
|
62
|
+
OpenAI.configure do |config|
|
63
|
+
config.access_token = ENV.fetch('OPENAI_ACCESS_TOKEN') if options[:ai_service] == :openai
|
64
|
+
# config.log_errors = true # Use for development
|
65
|
+
config.organization_id = ENV['OPENAI_ORGANIZATION_ID'] if ENV['OPENAI_ORGANIZATION_ID']
|
66
|
+
config.request_timeout = 240
|
67
|
+
if options[:ai_service] == :ollama
|
68
|
+
config.uri_base = ENV['OLLAMA_URL'] || 'http://localhost:11434'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
rescue KeyError => e
|
72
|
+
puts "Error: #{e.message}"
|
73
|
+
puts "Please set the OPENAI_ACCESS_TOKEN environment variable."
|
74
|
+
exit
|
75
|
+
end
|
76
|
+
|
77
|
+
def canned_content
|
78
|
+
FakeGenerator::FAKE_MESSAGES
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module Hyrum
|
6
|
+
class ScriptOptions
|
7
|
+
MANDATORY_OPTIONS = %i[message].freeze
|
8
|
+
|
9
|
+
attr_reader :options
|
10
|
+
|
11
|
+
def initialize(args)
|
12
|
+
@options = {}
|
13
|
+
@args = args
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse
|
17
|
+
OptionParser.new do |parser|
|
18
|
+
define_options(parser)
|
19
|
+
parser.parse!(@args)
|
20
|
+
end
|
21
|
+
enforce_mandatory_options
|
22
|
+
options
|
23
|
+
rescue OptionParser::InvalidOption => e
|
24
|
+
err = "Invalid option: #{e.message}"
|
25
|
+
rescue OptionParser::MissingArgument => e
|
26
|
+
err = "Missing argument for option: #{e.message}"
|
27
|
+
rescue OptionParser::InvalidArgument => e
|
28
|
+
err = "Invalid argument for option: #{e.message}"
|
29
|
+
ensure
|
30
|
+
if err
|
31
|
+
puts err
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def enforce_mandatory_options
|
39
|
+
missing = MANDATORY_OPTIONS.select { |param| options[param].nil? }
|
40
|
+
return if missing.empty?
|
41
|
+
|
42
|
+
raise OptionParser::MissingArgument, missing.join(', ')
|
43
|
+
end
|
44
|
+
|
45
|
+
def define_options(parser)
|
46
|
+
parser.banner = 'Usage: hyrum [options]'
|
47
|
+
|
48
|
+
verbosity_options(parser)
|
49
|
+
format_options(parser)
|
50
|
+
message_options(parser)
|
51
|
+
message_key_options(parser)
|
52
|
+
ai_service_options(parser)
|
53
|
+
on_tail_options(parser)
|
54
|
+
end
|
55
|
+
|
56
|
+
def on_tail_options(parser)
|
57
|
+
parser.on_tail('-h', '--help', 'Show this message') do
|
58
|
+
puts parser
|
59
|
+
exit
|
60
|
+
end
|
61
|
+
|
62
|
+
parser.on_tail('--version', 'Show version') do
|
63
|
+
puts Hyrum::VERSION
|
64
|
+
exit
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def ai_service_options(parser)
|
69
|
+
description = "AI service: one of #{Generators::AI_SERVICES.join(', ')}"
|
70
|
+
parser.on('-s SERVICE', '--service SERVICE', Generators::AI_SERVICES, description) do |service|
|
71
|
+
options[:ai_service] = service.to_sym
|
72
|
+
end
|
73
|
+
options[:ai_service] ||= :fake
|
74
|
+
|
75
|
+
default_model = Generators::AI_MODEL_DEFAULTS[options[:ai_service]]
|
76
|
+
description = 'AI model: must be a valid model for the selected service'
|
77
|
+
parser.on('-d MODEL', '--model MODEL', description) do |model|
|
78
|
+
options[:ai_model] = model.to_sym
|
79
|
+
end
|
80
|
+
options[:ai_model] ||= default_model
|
81
|
+
end
|
82
|
+
|
83
|
+
def message_key_options(parser)
|
84
|
+
parser.on('-k KEY', '--key KEY', 'Message key') do |key|
|
85
|
+
options[:key] = key
|
86
|
+
end
|
87
|
+
options[:key] ||= 'status'
|
88
|
+
end
|
89
|
+
|
90
|
+
def message_options(parser)
|
91
|
+
parser.on('-m MESSAGE', '--message MESSAGE', 'Status message') do |message|
|
92
|
+
options[:message] = message
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def verbosity_options(parser)
|
97
|
+
parser.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
|
98
|
+
options[:verbose] = v
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def format_options(parser)
|
103
|
+
formats = Formats::FORMATS
|
104
|
+
description = 'Output format. Supported formats are:'
|
105
|
+
supported = formats.join(', ')
|
106
|
+
parser.on('-f FORMAT', '--format FORMAT', formats, description, supported) do |format|
|
107
|
+
options[:format] = format
|
108
|
+
end
|
109
|
+
options[:format] ||= :text
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/hyrum.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'zeitwerk'
|
5
|
+
loader = Zeitwerk::Loader.for_gem
|
6
|
+
loader.setup
|
7
|
+
|
8
|
+
module Hyrum
|
9
|
+
def self.run(args)
|
10
|
+
options = ScriptOptions.new(args).parse
|
11
|
+
generator_opts = options.slice(:message, :key, :ai_service, :ai_model, :verbose)
|
12
|
+
formatter_opts = options.slice(:format, :verbose)
|
13
|
+
|
14
|
+
puts "Options: #{options.inspect}" if options[:verbose]
|
15
|
+
formatter = Formats::Formatter.new(formatter_opts)
|
16
|
+
message_generator = Generators::MessageGenerator.create(generator_opts)
|
17
|
+
messages = message_generator.generate
|
18
|
+
output = formatter.format(messages)
|
19
|
+
puts output
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
loader.eager_load
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hyrum
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tracy Atteberry
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-12-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ruby-openai
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: zeitwerk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.7'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.7'
|
41
|
+
description: A multi-language code generator to cope with Hyrum's law
|
42
|
+
email:
|
43
|
+
- tracy@tracyatteberry.com
|
44
|
+
executables:
|
45
|
+
- hyrum
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files:
|
48
|
+
- README.md
|
49
|
+
files:
|
50
|
+
- README.md
|
51
|
+
- bin/console
|
52
|
+
- bin/hyrum
|
53
|
+
- bin/setup
|
54
|
+
- lib/hyrum.rb
|
55
|
+
- lib/hyrum/formats/formatter.rb
|
56
|
+
- lib/hyrum/formats/templates/java.erb
|
57
|
+
- lib/hyrum/formats/templates/javascript.erb
|
58
|
+
- lib/hyrum/formats/templates/json.erb
|
59
|
+
- lib/hyrum/formats/templates/python.erb
|
60
|
+
- lib/hyrum/formats/templates/ruby.erb
|
61
|
+
- lib/hyrum/formats/templates/text.erb
|
62
|
+
- lib/hyrum/generators/fake_generator.rb
|
63
|
+
- lib/hyrum/generators/message_generator.rb
|
64
|
+
- lib/hyrum/generators/openai_generator.rb
|
65
|
+
- lib/hyrum/script_options.rb
|
66
|
+
- lib/hyrum/version.rb
|
67
|
+
homepage: https://github.com/grymoire7/hyrum
|
68
|
+
licenses:
|
69
|
+
- MIT
|
70
|
+
metadata:
|
71
|
+
rubygems_mfa_required: 'true'
|
72
|
+
changelog_uri: https://github.com/grymoire7/hyrum/blob/main/CHANGELOG.md
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options:
|
75
|
+
- "--title"
|
76
|
+
- Hyrum - Hyrum's Law Code Generator
|
77
|
+
- "--main"
|
78
|
+
- README.md
|
79
|
+
- "--line-numbers"
|
80
|
+
- "--inline-source"
|
81
|
+
- "--quiet"
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 3.1.0
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubygems_version: 3.5.23
|
96
|
+
signing_key:
|
97
|
+
specification_version: 4
|
98
|
+
summary: A simple Ruby gem
|
99
|
+
test_files: []
|