n2b 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '00568c10ff4e0c1684ef83fe6579d40ac1d89623fbd704db4810b3b031fffb40'
4
- data.tar.gz: e6f22194fadffcf43ed553bb37693607a49b2a9fe2a614748d9b3ae8e415eb30
3
+ metadata.gz: a202dae3ba4645542ae044ca8c2e924c34673907ecbcc4bd123ac054fb1493eb
4
+ data.tar.gz: b308ead565a4ce70433f52551370246d2c4f1043e76d314f0ba5d4f7224f7a92
5
5
  SHA512:
6
- metadata.gz: f40a0f2829fc6b2cfcdf512c08bf2a754d41e3fed6bdbbfbe6d63cfde7aa9318b6f8256dea2655e0dfea5485e5bfc358faaae3ee643905ed1c873d2770e62c21
7
- data.tar.gz: 054066fa79caf2cdc0077017abf6e01d4b06ed361199d0d9471549a232c77af63adf613642056f12aceb300894329ed832c607a4dcf4e537ba5dfa9a4d0fc6fb
6
+ metadata.gz: 9bcb6e988ca781399ff84c8189aa37c4ddface4cc6dc5474b9f22793b10b5de10c82bba7c8d2475bf0f0a030ea4c638fb4136ec2b5d98871a84affb60a5a5ab7
7
+ data.tar.gz: 598a4d9a0a684a8d95bbb644d2ba799b32e617868be56cc4e5d661d4ee6646cd42389c0315a60175546faadcdb0c4fde1742ca9270e3b9a9500d3f77c933d935
data/README.md CHANGED
@@ -1,11 +1,12 @@
1
1
  # N2B: Natural Language to Bash Commands Converter
2
2
 
3
- N2B (Natural to Bash) is a Ruby gem that converts natural language instructions into executable shell commands using the Claude AI API. It's designed to help users quickly generate shell commands without needing to remember exact syntax.
3
+ N2B (Natural to Bash) is a Ruby gem that converts natural language instructions into executable shell commands using the Claude AI or OpenAI API. It's designed to help users quickly generate shell commands without needing to remember exact syntax.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - Convert natural language to shell commands
8
8
  - Support for multiple Claude AI models (Haiku, Sonnet, Sonnet 3.5)
9
+ - Support for OpenAI models
9
10
  - Option to execute generated commands directly
10
11
  - Configurable privacy settings
11
12
  - Shell history integration
@@ -0,0 +1,50 @@
1
+ module N2M
2
+ module Llm
3
+ class Claude
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' }
6
+
7
+ def initialize(config)
8
+ @config = config
9
+ end
10
+
11
+ def make_request( content)
12
+ uri = URI.parse('https://api.anthropic.com/v1/messages')
13
+ request = Net::HTTP::Post.new(uri)
14
+ request.content_type = 'application/json'
15
+ request['X-API-Key'] = @config['access_key']
16
+ request['anthropic-version'] = '2023-06-01'
17
+
18
+ request.body = JSON.dump({
19
+ "model" => MODELS[@config['model']],
20
+ "max_tokens" => 1024,
21
+ "messages" => [
22
+ {
23
+ "role" => "user",
24
+ "content" => content
25
+ }
26
+ ]
27
+ })
28
+
29
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
30
+ http.request(request)
31
+ end
32
+ # check for errors
33
+ if response.code != '200'
34
+ puts "Error: #{response.code} #{response.message}"
35
+ puts response.body
36
+ exit 1
37
+ end
38
+ answer = JSON.parse(response.body)['content'].first['text']
39
+ begin
40
+ # removee everything before the first { and after the last }
41
+ answer = answer.sub(/.*\{(.*)\}.*/m, '{\1}')
42
+ answer = JSON.parse(answer)
43
+ rescue JSON::ParserError
44
+ answer = { 'commands' => answer.split("\n"), explanation: answer}
45
+ end
46
+ answer
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,53 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'uri'
4
+
5
+ module N2M
6
+ module Llm
7
+ class OpenAi
8
+ API_URI = URI.parse('https://api.openai.com/v1/chat/completions')
9
+ MODELS = { 'gpt-4o' => 'gpt-4o', 'gpt-35' => 'gpt-3.5-turbo-1106' }
10
+
11
+ def initialize(config)
12
+ @config = config
13
+ end
14
+
15
+ def make_request(content)
16
+ request = Net::HTTP::Post.new(API_URI)
17
+ request.content_type = 'application/json'
18
+ request['Authorization'] = "Bearer #{@config['access_key']}"
19
+
20
+ request.body = JSON.dump({
21
+ "model" => MODELS[@config['model']],
22
+ response_format: { type: 'json_object' },
23
+ "messages" => [
24
+ {
25
+ "role" => "user",
26
+ "content" => content
27
+ }]
28
+ })
29
+
30
+ response = Net::HTTP.start(API_URI.hostname, API_URI.port, use_ssl: true) do |http|
31
+ http.request(request)
32
+ end
33
+
34
+ # check for errors
35
+ if response.code != '200'
36
+ puts "Error: #{response.code} #{response.message}"
37
+ puts response.body
38
+ exit 1
39
+ end
40
+ puts JSON.parse(response.body)
41
+ answer = JSON.parse(response.body)['choices'].first['message']['content']
42
+ begin
43
+ # remove everything before the first { and after the last }
44
+ answer = answer.sub(/.*\{(.*)\}.*/m, '{\1}')
45
+ answer = JSON.parse(answer)
46
+ rescue JSON::ParserError
47
+ answer = { 'commands' => answer.split("\n"), explanation: answer }
48
+ end
49
+ answer
50
+ end
51
+ end
52
+ end
53
+ end
data/lib/n2b/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # lib/n2b/version.rb
2
2
  module N2B
3
- VERSION = "0.1.2"
3
+ VERSION = "0.1.3"
4
4
  end
data/lib/n2b.rb CHANGED
@@ -1,19 +1,19 @@
1
1
  # lib/n2b.rb
2
- require "n2b/version"
3
2
  require 'net/http'
4
3
  require 'uri'
5
4
  require 'json'
6
5
  require 'optparse'
7
6
  require 'yaml'
8
7
  require 'fileutils'
9
-
8
+ require 'n2b/version'
9
+ require 'n2b/llm/claude'
10
+ require 'n2b/llm/open_ai'
10
11
  module N2B
11
12
  class Error < StandardError; end
12
13
 
13
14
  CONFIG_FILE = File.expand_path('~/.n2b/config.yml')
14
15
  HISTORY_FILE = File.expand_path('~/.n2b/history')
15
- MODELS = { 'haiku' => 'claude-3-haiku-20240307', 'sonnet' => 'claude-3-sonnet-20240229', 'sonnet35' => 'claude-3-5-sonnet-20240620' }
16
-
16
+
17
17
  class CLI
18
18
  def self.run(args)
19
19
  new(args).execute
@@ -67,27 +67,36 @@ module N2B
67
67
  config = load_config
68
68
  api_key = ENV['CLAUDE_API_KEY'] || config['access_key']
69
69
  model = config['model'] || 'sonnet35'
70
+
70
71
  if api_key.nil? || api_key == '' || reconfigure
71
- print "Enter your Claude API key: #{ api_key.nil? || api_key.empty? ? '' : '(leave blank to keep the current key '+api_key[0..10]+'...)' }"
72
+ print "choose a language model to use (claude, openai): "
73
+ llm = $stdin.gets.chomp
74
+ unless ['claude', 'openai'].include?(llm)
75
+ puts "Invalid language model. Choose from: claude, openai"
76
+ exit 1
77
+ end
78
+ llm_class = llm == 'openai' ? N2M::Llm::OpenAi : N2M::Llm::Claude
79
+
80
+ print "Enter your #{llm} API key: #{ api_key.nil? || api_key.empty? ? '' : '(leave blank to keep the current key '+api_key[0..10]+'...)' }"
72
81
  api_key = $stdin.gets.chomp
73
82
  api_key = config['access_key'] if api_key.empty?
74
- print "Choose a model (haiku, sonnet, sonnet35 (default)): "
83
+ print "Choose a model (#{ llm_class::MODELS.keys }, #{ llm_class::MODELS.keys.first } default): "
75
84
  model = $stdin.gets.chomp
76
- model = 'sonnet35' if model.empty?
77
- config['llm'] ||= 'anthropic'
85
+ model = llm_class::MODELS.keys.first if model.empty?
86
+ config['llm'] = llm
78
87
  config['access_key'] = api_key
79
88
  config['model'] = model
80
- unless MODELS.keys.include?(model)
81
- puts "Invalid model. Choose from: #{MODELS.keys.join(', ')}"
89
+ unless llm_class::MODELS.keys.include?(model)
90
+ puts "Invalid model. Choose from: #{llm_class::MODELS.keys.join(', ')}"
82
91
  exit 1
83
92
  end
84
93
 
85
94
  config['privacy'] ||= {}
86
- print "Do you want to send your shell history to Claude? (y/n): "
95
+ print "Do you want to send your shell history to #{llm}? (y/n): "
87
96
  config['privacy']['send_shell_history'] = $stdin.gets.chomp == 'y'
88
- print "Do you want to send your past requests and answers to Claude? (y/n): "
97
+ print "Do you want to send your past requests and answers to #{llm}? (y/n): "
89
98
  config['privacy']['send_llm_history'] = $stdin.gets.chomp == 'y'
90
- print "Do you want to send your current directory to Claude? (y/n): "
99
+ print "Do you want to send your current directory to #{llm}? (y/n): "
91
100
  config['privacy']['send_current_directory'] = $stdin.gets.chomp == 'y'
92
101
  print "Do you want to append the commands to your shell history? (y/n): "
93
102
  config['append_to_shell_history'] = $stdin.gets.chomp == 'y'
@@ -115,11 +124,9 @@ module N2B
115
124
  end
116
125
 
117
126
  def call_llm(prompt, config)
118
- uri = URI.parse('https://api.anthropic.com/v1/messages')
119
- request = Net::HTTP::Post.new(uri)
120
- request.content_type = 'application/json'
121
- request['X-API-Key'] = config['access_key']
122
- request['anthropic-version'] = '2023-06-01'
127
+
128
+ llm = config['llm'] == 'openai' ? N2M::Llm::OpenAi.new(config) : N2M::Llm::Claude.new(config)
129
+
123
130
  content = <<-EOF
124
131
  Translate the following natural language command to bash commands: #{prompt}\n\nProvide only the #{get_user_shell} commands for #{ get_user_os }. the commands should be separated by newlines.
125
132
  #{' the user is in directory'+Dir.pwd if config['privacy']['send_current_directory']}.
@@ -130,34 +137,9 @@ module N2B
130
137
  { "commands": [ "echo 'Hello, World!'" ], "explanation": "This command prints 'Hello, World!' to the terminal."}
131
138
  EOF
132
139
 
133
- request.body = JSON.dump({
134
- "model" => MODELS[config['model']],
135
- "max_tokens" => 1024,
136
- "messages" => [
137
- {
138
- "role" => "user",
139
- "content" => content
140
- }
141
- ]
142
- })
143
-
144
- response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
145
- http.request(request)
146
- end
147
- # check for errors
148
- if response.code != '200'
149
- puts "Error: #{response.code} #{response.message}"
150
- puts response.body
151
- exit 1
152
- end
153
- answer = JSON.parse(response.body)['content'].first['text']
154
- begin
155
- # removee everything before the first { and after the last }
156
- answer = answer.sub(/.*\{(.*)\}.*/m, '{\1}')
157
- answer = JSON.parse(answer)
158
- rescue JSON::ParserError
159
- answer = { 'commands' => answer.split("\n"), explanation: answer}
160
- end
140
+
141
+ answer = llm.make_request(content)
142
+
161
143
  append_to_llm_history_file("#{prompt}\n#{answer}")
162
144
  answer
163
145
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: n2b
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Nothegger
@@ -64,6 +64,8 @@ files:
64
64
  - README.md
65
65
  - bin/n2b
66
66
  - lib/n2b.rb
67
+ - lib/n2b/llm/claude.rb
68
+ - lib/n2b/llm/open_ai.rb
67
69
  - lib/n2b/version.rb
68
70
  homepage: https://github.com/stefan-kp/n2b
69
71
  licenses:
@@ -87,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
87
89
  - !ruby/object:Gem::Version
88
90
  version: '0'
89
91
  requirements: []
90
- rubygems_version: 3.4.2
92
+ rubygems_version: 3.0.9
91
93
  signing_key:
92
94
  specification_version: 4
93
95
  summary: Convert natural language to bash commands