n2b 0.2.2 → 0.2.4
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/README.md +44 -31
- data/lib/n2b/base.rb +15 -10
- data/lib/n2b/irb.rb +24 -3
- data/lib/n2b/llm/claude.rb +1 -1
- data/lib/n2b/llm/gemini.rb +69 -0
- data/lib/n2b/version.rb +1 -1
- data/lib/n2b.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf304be008b27bf4336085ec39f75fc8ac5207c6e17d442d5129a366795629eb
|
4
|
+
data.tar.gz: f436a7dc4a59f2840b023d7e406aa5fa2aa02dee7ebb10f48c982efaaf489546
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 770f49a5df340f3f9eae3f9072eafcbd9e8de6e2a5a227da3a96c67ebb22ca998411d1098921a375a9203fe446593ac8b15cfc3b6c6e6d13503a48957fed29c9
|
7
|
+
data.tar.gz: 88720e0bae3adf499830681214c142ea96787f3fbae01fcfe276593ce3860f65127783293cf2c1c993725747ad54165e260473f84d172fb290490165cd587708
|
data/README.md
CHANGED
@@ -13,61 +13,74 @@ N2B (Natural Language to Bash & Ruby) is a Ruby gem that leverages AI to convert
|
|
13
13
|
|
14
14
|
## Installation
|
15
15
|
|
16
|
-
|
16
|
+
```bash
|
17
|
+
gem install n2b
|
18
|
+
```
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
### In IRB/Rails Console
|
23
|
+
|
24
|
+
First, require and extend the N2B module:
|
17
25
|
|
18
26
|
```ruby
|
19
|
-
|
27
|
+
require 'n2b'
|
28
|
+
extend N2B::IRB
|
20
29
|
```
|
21
30
|
|
22
|
-
|
31
|
+
For automatic loading in every IRB session, add these lines to your `~/.irbrc`:
|
23
32
|
|
24
|
-
```
|
25
|
-
|
33
|
+
```ruby
|
34
|
+
require 'n2b'
|
35
|
+
extend N2B::IRB
|
26
36
|
```
|
27
37
|
|
28
|
-
|
38
|
+
After loading, you can use the following commands:
|
29
39
|
|
30
|
-
|
31
|
-
|
40
|
+
- `n2r` - For general Ruby assistance
|
41
|
+
- `n2rrbit` - For Errbit error analysis
|
42
|
+
- `n2rscrum` - For generating Scrum tickets
|
43
|
+
|
44
|
+
### Examples
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# Get help with a Ruby question
|
48
|
+
n2r "How do I parse JSON in Ruby?"
|
49
|
+
|
50
|
+
# Analyze an Errbit error
|
51
|
+
n2rrbit(url: "your_errbit_url", cookie: "your_cookie")
|
52
|
+
|
53
|
+
# Generate a Scrum ticket
|
54
|
+
n2rscrum "Create a user authentication system"
|
32
55
|
```
|
33
56
|
|
34
57
|
## Configuration
|
35
58
|
|
36
|
-
Create a config file at `~/.n2b/config.yml` with your API keys:
|
59
|
+
Create a config file at `~/.n2b/config.yml` with your API keys. You can also use a custom config file by setting the `N2B_CONFIG_FILE` environment variable:
|
37
60
|
|
61
|
+
```bash
|
62
|
+
export N2B_CONFIG_FILE=/path/to/your/config.yml
|
63
|
+
```
|
64
|
+
|
65
|
+
Example config file:
|
38
66
|
```yaml
|
39
|
-
llm: claude # or openai
|
67
|
+
llm: claude # or openai, gemini
|
40
68
|
claude:
|
41
69
|
key: your-anthropic-api-key
|
42
70
|
model: claude-3-opus-20240229 # or opus, haiku, sonnet
|
43
71
|
openai:
|
44
72
|
key: your-openai-api-key
|
45
73
|
model: gpt-4 # or gpt-3.5-turbo
|
74
|
+
gemini:
|
75
|
+
key: your-google-api-key
|
76
|
+
model: gemini-flash # uses gemini-2.0-flash model
|
46
77
|
```
|
47
78
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
```ruby
|
53
|
-
require 'n2b'
|
54
|
-
|
55
|
-
# Convert a natural language instruction to a bash command
|
56
|
-
N2B::Base.new.n2b("list all jpg files in the current directory")
|
57
|
-
# => find . -name "*.jpg"
|
58
|
-
```
|
59
|
-
|
60
|
-
### Generate Ruby Code
|
61
|
-
|
62
|
-
```ruby
|
63
|
-
require 'n2b'
|
64
|
-
|
65
|
-
# In an IRB console
|
66
|
-
n2r("create a function that calculates fibonacci numbers")
|
79
|
+
You can also set the history file location using the `N2B_HISTORY_FILE` environment variable:
|
80
|
+
```bash
|
81
|
+
export N2B_HISTORY_FILE=/path/to/your/history
|
67
82
|
```
|
68
83
|
|
69
|
-
This will output both the code and an explanation:
|
70
|
-
|
71
84
|
## Quick Example N2B
|
72
85
|
|
73
86
|
```
|
data/lib/n2b/base.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module N2B
|
2
2
|
class Base
|
3
3
|
|
4
|
-
CONFIG_FILE = File.expand_path('~/.n2b/config.yml')
|
5
|
-
HISTORY_FILE = File.expand_path('~/.n2b/history')
|
4
|
+
CONFIG_FILE = ENV['N2B_CONFIG_FILE'] || File.expand_path('~/.n2b/config.yml')
|
5
|
+
HISTORY_FILE = ENV['N2B_HISTORY_FILE'] || File.expand_path('~/.n2b/history')
|
6
6
|
|
7
7
|
def load_config
|
8
8
|
if File.exist?(CONFIG_FILE)
|
@@ -18,16 +18,24 @@ module N2B
|
|
18
18
|
model = config['model'] || 'sonnet35'
|
19
19
|
|
20
20
|
if api_key.nil? || api_key == '' || reconfigure
|
21
|
-
print "choose a language model to use (1:claude, 2:openai) #{ config['llm'] }: "
|
21
|
+
print "choose a language model to use (1:claude, 2:openai, 3:gemini) #{ config['llm'] }: "
|
22
22
|
llm = $stdin.gets.chomp
|
23
23
|
llm = config['llm'] if llm.empty?
|
24
|
-
unless ['claude', 'openai','1','2'].include?(llm)
|
25
|
-
puts "Invalid language model. Choose from: claude, openai"
|
24
|
+
unless ['claude', 'openai', 'gemini', '1', '2', '3'].include?(llm)
|
25
|
+
puts "Invalid language model. Choose from: claude, openai, gemini"
|
26
26
|
exit 1
|
27
27
|
end
|
28
28
|
llm = 'claude' if llm == '1'
|
29
29
|
llm = 'openai' if llm == '2'
|
30
|
-
|
30
|
+
llm = 'gemini' if llm == '3'
|
31
|
+
llm_class = case llm
|
32
|
+
when 'openai'
|
33
|
+
N2M::Llm::OpenAi
|
34
|
+
when 'gemini'
|
35
|
+
N2M::Llm::Gemini
|
36
|
+
else
|
37
|
+
N2M::Llm::Claude
|
38
|
+
end
|
31
39
|
|
32
40
|
print "Enter your #{llm} API key: #{ api_key.nil? || api_key.empty? ? '' : '(leave blank to keep the current key '+api_key[0..10]+'...)' }"
|
33
41
|
api_key = $stdin.gets.chomp
|
@@ -50,11 +58,8 @@ module N2B
|
|
50
58
|
config['append_to_shell_history'] = false
|
51
59
|
puts "Current configuration: #{config['privacy']}"
|
52
60
|
FileUtils.mkdir_p(File.dirname(CONFIG_FILE)) unless File.exist?(File.dirname(CONFIG_FILE))
|
53
|
-
File.
|
54
|
-
f.write(config.to_yaml )
|
55
|
-
end
|
61
|
+
File.write(CONFIG_FILE, config.to_yaml)
|
56
62
|
end
|
57
|
-
|
58
63
|
config
|
59
64
|
end
|
60
65
|
end
|
data/lib/n2b/irb.rb
CHANGED
@@ -17,7 +17,14 @@ module N2B
|
|
17
17
|
|
18
18
|
def n2r(input_string='', files: [], exception: nil, log: false)
|
19
19
|
config = N2B::Base.new.get_config
|
20
|
-
llm = config['llm']
|
20
|
+
llm = case config['llm']
|
21
|
+
when 'openai'
|
22
|
+
N2M::Llm::OpenAi.new(config)
|
23
|
+
when 'gemini'
|
24
|
+
N2M::Llm::Gemini.new(config)
|
25
|
+
else
|
26
|
+
N2M::Llm::Claude.new(config)
|
27
|
+
end
|
21
28
|
# detect if inside rails console
|
22
29
|
console = case
|
23
30
|
when defined?(Rails) && Rails.respond_to?(:application)
|
@@ -586,7 +593,14 @@ module N2B
|
|
586
593
|
|
587
594
|
def analyze_error(error_info, related_files)
|
588
595
|
config = N2B::Base.new.get_config
|
589
|
-
llm = config['llm']
|
596
|
+
llm = case config['llm']
|
597
|
+
when 'openai'
|
598
|
+
N2M::Llm::OpenAi.new(config)
|
599
|
+
when 'gemini'
|
600
|
+
N2M::Llm::Gemini.new(config)
|
601
|
+
else
|
602
|
+
N2M::Llm::Claude.new(config)
|
603
|
+
end
|
590
604
|
|
591
605
|
# Build file content section, showing context if available
|
592
606
|
file_content_section = related_files.map do |file_path, content|
|
@@ -651,7 +665,14 @@ module N2B
|
|
651
665
|
|
652
666
|
def generate_error_ticket(error_info, related_files = {}, url = nil)
|
653
667
|
config = N2B::Base.new.get_config
|
654
|
-
llm = config['llm']
|
668
|
+
llm = case config['llm']
|
669
|
+
when 'openai'
|
670
|
+
N2M::Llm::OpenAi.new(config)
|
671
|
+
when 'gemini'
|
672
|
+
N2M::Llm::Gemini.new(config)
|
673
|
+
else
|
674
|
+
N2M::Llm::Claude.new(config)
|
675
|
+
end
|
655
676
|
|
656
677
|
# Build file content section, showing context if available
|
657
678
|
file_content_section = related_files.map do |file_path, content|
|
data/lib/n2b/llm/claude.rb
CHANGED
@@ -2,7 +2,7 @@ module N2M
|
|
2
2
|
module Llm
|
3
3
|
class Claude
|
4
4
|
API_URI = URI.parse('https://api.anthropic.com/v1/messages')
|
5
|
-
MODELS = { 'haiku' => 'claude-3-haiku-20240307', 'sonnet' => 'claude-3-sonnet-20240229', 'sonnet35' => 'claude-3-5-sonnet-20240620' }
|
5
|
+
MODELS = { 'haiku' => 'claude-3-haiku-20240307', 'sonnet' => 'claude-3-sonnet-20240229', 'sonnet35' => 'claude-3-5-sonnet-20240620', "sonnet37" => "claude-3-7-sonnet-20250219" }
|
6
6
|
|
7
7
|
def initialize(config)
|
8
8
|
@config = config
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module N2M
|
6
|
+
module Llm
|
7
|
+
class Gemini
|
8
|
+
API_URI = URI.parse('https://generativelanguage.googleapis.com/v1beta/models')
|
9
|
+
MODELS = {
|
10
|
+
'gemini-flash' => 'gemini-2.0-flash'
|
11
|
+
}
|
12
|
+
|
13
|
+
def initialize(config)
|
14
|
+
@config = config
|
15
|
+
end
|
16
|
+
|
17
|
+
def make_request(content)
|
18
|
+
model = MODELS[@config['model']] || 'gemini-flash'
|
19
|
+
uri = URI.parse("#{API_URI}/#{model}:generateContent?key=#{@config['access_key']}")
|
20
|
+
|
21
|
+
request = Net::HTTP::Post.new(uri)
|
22
|
+
request.content_type = 'application/json'
|
23
|
+
|
24
|
+
request.body = JSON.dump({
|
25
|
+
"contents" => [{
|
26
|
+
"parts" => [{
|
27
|
+
"text" => content
|
28
|
+
}]
|
29
|
+
}]
|
30
|
+
})
|
31
|
+
|
32
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
33
|
+
http.request(request)
|
34
|
+
end
|
35
|
+
|
36
|
+
# check for errors
|
37
|
+
if response.code != '200'
|
38
|
+
puts "Error: #{response.code} #{response.message}"
|
39
|
+
puts response.body
|
40
|
+
puts "Config: #{@config.inspect}"
|
41
|
+
exit 1
|
42
|
+
end
|
43
|
+
|
44
|
+
parsed_response = JSON.parse(response.body)
|
45
|
+
answer = parsed_response['candidates'].first['content']['parts'].first['text']
|
46
|
+
|
47
|
+
begin
|
48
|
+
# Try to parse as JSON if it looks like JSON
|
49
|
+
if answer.strip.start_with?('{') && answer.strip.end_with?('}')
|
50
|
+
answer = JSON.parse(answer)
|
51
|
+
else
|
52
|
+
# If not JSON, wrap it in our expected format
|
53
|
+
answer = {
|
54
|
+
'explanation' => answer,
|
55
|
+
'code' => nil
|
56
|
+
}
|
57
|
+
end
|
58
|
+
rescue JSON::ParserError
|
59
|
+
# If JSON parsing fails, wrap the text in our expected format
|
60
|
+
answer = {
|
61
|
+
'explanation' => answer,
|
62
|
+
'code' => nil
|
63
|
+
}
|
64
|
+
end
|
65
|
+
answer
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/n2b/version.rb
CHANGED
data/lib/n2b.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: n2b
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Nothegger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-03-
|
11
|
+
date: 2025-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- lib/n2b/cli.rb
|
70
70
|
- lib/n2b/irb.rb
|
71
71
|
- lib/n2b/llm/claude.rb
|
72
|
+
- lib/n2b/llm/gemini.rb
|
72
73
|
- lib/n2b/llm/open_ai.rb
|
73
74
|
- lib/n2b/version.rb
|
74
75
|
homepage: https://github.com/stefan-kp/n2b
|