ask-ai 0.0.5 → 1.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f43705932c791d2b495230b8ac87567a23dafddd7c76fbbdc9cd0d88d960aac7
4
- data.tar.gz: c8376477e6a4261f04ced1c99ccc07d433551e1613c317b51ab1b21b53e50b58
3
+ metadata.gz: 3d0033fda00a5a8399a6e9b71c75338b02269df54edb791bd82215e939492d15
4
+ data.tar.gz: 391d4cc9fe240289358f2489b61224b8d0706254174f9ad4a5ec7a109b7b3a89
5
5
  SHA512:
6
- metadata.gz: 26841ece9e0ee1cb05d1df0d6cbf1b20db94d18daf74be4decf82599bf3614f5a6823a2ae3398a26d9a4df480c9eedb51285934bd674d4f3b2eac5355b617da1
7
- data.tar.gz: 0ecfbc09c3944f62ef1cfdc3ddbef975d7b2cf3ea69913bff416ba25f6df063436bdbf7359bf40ca9b80fa4ce0d4dfc74d54d79b487f1f8c9b22365a2027c70e
6
+ metadata.gz: 6f749624e465e35a8ebcae8b7b9b312d1f4ffdbd8e390043a0fc87dae5eaf680931975eb87369eba855043eff52206c2daf47af95fc5136e7fb0134520275a28
7
+ data.tar.gz: 81d6f56f465808352087d5f53bab44b7a1af2d63292dbba4b5fab7f91b44fd3d78c6a591718d142f6348c6ab78bae8a54d84b2cba73e2164efd32dd3b27d4e1d
data/config/config.yml CHANGED
@@ -1,3 +0,0 @@
1
- ---
2
- TEMPERATURE: 0.7
3
- CONTEXT_LENGTH: 12
data/lib/config.rb CHANGED
@@ -1,65 +1,110 @@
1
1
  require_relative './files.rb'
2
2
 
3
3
  module Config
4
- include Files
5
- def self.load_key()
6
- config = YAML.load_file(Files.config_path)
4
+ extend Files
5
+ def load_key()
6
+ config = YAML.load_file(config_path)
7
7
  config['OPENAI_API_KEY']
8
8
  end
9
9
 
10
- def self.save_key(api_key)
11
- config = YAML.load_file(Files.config_path)
10
+ def save_key(api_key)
11
+ config = YAML.load_file(config_path)
12
+ if config == false
13
+ config = {}
14
+ end
12
15
  config['OPENAI_API_KEY'] = api_key
13
- File.open(Files.config_path, 'w') { |f| YAML.dump(config, f) }
16
+ File.open(config_path, 'w') { |f| YAML.dump(config, f) }
14
17
  end
15
18
 
16
- def self.load_temperature
17
- config = YAML.load_file(Files.config_path)
18
- config['TEMPERATURE']
19
+ def load_temperature
20
+ config = YAML.load_file(config_path)
21
+ unless config == false
22
+ config['TEMPERATURE']
23
+ end
19
24
  end
20
25
 
21
- def self.save_temperature(temperature)
22
- config = YAML.load_file(Files.config_path)
26
+ def save_temperature(temperature)
27
+ config = YAML.load_file(config_path)
28
+ if config == false
29
+ config = {}
30
+ end
23
31
  config['TEMPERATURE'] = temperature.to_f
24
- File.open(Files.config_path, 'w') { |f| YAML.dump(config, f) }
32
+ File.open(config_path, 'w') { |f| YAML.dump(config, f) }
25
33
  end
26
34
 
27
- def self.load_context_length
28
- config = YAML.load_file(Files.config_path)
29
- config['CONTEXT_LENGTH']
35
+ def load_context_length
36
+ config = YAML.load_file(config_path)
37
+ unless config == false
38
+ config['CONTEXT_LENGTH']
39
+ end
30
40
  end
31
41
 
32
- def self.save_context_length(context_length)
33
- config = YAML.load_file(Files.config_path)
42
+ def save_context_length(context_length)
43
+ config = YAML.load_file(config_path)
44
+ if config == false
45
+ config = {}
46
+ end
34
47
  config['CONTEXT_LENGTH'] = context_length.to_i
35
- File.open(Files.config_path, 'w') { |f| YAML.dump(config, f) }
48
+ File.open(config_path, 'w') { |f| YAML.dump(config, f) }
36
49
  end
37
50
 
38
- def self.set_config(value)
39
- puts 'value',value
51
+ def set_config(value)
40
52
  if value.include?('key')
41
53
  stript_value = value.sub(/^key/, '').strip
42
54
  if stript_value.empty?
43
- puts 'No API key given'
44
- exit
55
+ return 'No API key given'
45
56
  end
46
57
  save_key(stript_value)
58
+ return 'API key saved'
47
59
  elsif value.include?('temp')
48
- stript_value = value.sub(/^temp/, '').strip
49
- if stript_value.empty?
50
- puts 'No temperature given'
51
- exit
60
+ stript_value = value.sub(/^temp/, '').strip.to_f
61
+ if stript_value.to_f > 1.0 || stript_value.to_f < 0.1
62
+ return 'Temperature must be between 0.1 and 1.0'
52
63
  end
53
64
  save_temperature(stript_value)
65
+ return 'Temperature saved'
54
66
  elsif value.include?('context')
55
- stript_value = value.sub(/^context/, '').strip
56
- if stript_value.empty?
57
- puts 'No context length given'
58
- exit
67
+ stript_value = value.sub(/^context/, '').strip.to_i
68
+ if stript_value.to_i > 100 || stript_value.to_i < 1
69
+ return 'Context length must be between 1 and 100'
59
70
  end
60
71
  save_context_length(stript_value)
72
+ return 'Context length saved'
61
73
  else
62
- puts 'Invalid config value'
74
+ return 'Invalid config value'
63
75
  end
64
76
  end
77
+
78
+ def set_key(api_key: nil)
79
+ if api_key.nil?
80
+ log("Setting API key...")
81
+ log("Enter API key: (or press enter to exit)")
82
+
83
+ while input = Readline.readline("> ", true) do
84
+ if input.empty?
85
+ log("Exiting.")
86
+ exit
87
+ else
88
+ api_key = input.strip
89
+ break
90
+ end
91
+ end
92
+ log("Saving API key...")
93
+ end
94
+
95
+ FileUtils.mkdir_p(File.dirname(config_path))
96
+ File.open(config_path, "w") do |f|
97
+ f.write(YAML.dump({ "OPENAI_API_KEY" => api_key }))
98
+ end
99
+ log("API key saved.")
100
+ log("")
101
+ end
102
+
103
+ def load_env()
104
+ #Config.load_key()
105
+ YAML.load(File.read(config_path))
106
+
107
+ rescue Errno::ENOENT
108
+ log("No config.yml found.")
109
+ end
65
110
  end
data/lib/context.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  require_relative './files.rb'
2
2
  require_relative './config.rb'
3
3
  class Context
4
- include Files, Config
4
+ extend Files, Config, Logging
5
5
  def self.load_context()
6
- if File.exist?(Files.context_path)
7
- conversation = File.readlines(Files.context_path).map { |line| JSON.parse(line) }
6
+ if File.exist?(context_path)
7
+ conversation = File.readlines(context_path).map { |line| JSON.parse(line) }
8
8
  else
9
9
  conversation = []
10
10
  end
@@ -27,30 +27,30 @@ class Context
27
27
  ## Here we save the context to a file.
28
28
  ## Max 10 previous Q / A to save tokens.
29
29
  def self.save_context(context)
30
+ return if context.nil?
30
31
  tmp_arr = []
31
- unless File.exist?(Files.context_path)
32
- File.open(Files.context_path, "w") {}
32
+ unless File.exist?(context_path)
33
+ File.open(context_path, "w") {}
33
34
  end
34
- File.readlines(Files.context_path).map { |line| tmp_arr.push(JSON.parse(line)) }
35
- length = Config.load_context_length().nil? ? 10 : Config.load_context_length()
35
+ File.readlines(context_path).map { |line| tmp_arr.push(JSON.parse(line)) }
36
+ length = load_context_length().nil? ? 10 : load_context_length()
36
37
  if tmp_arr.length > length
37
38
  tmp_arr.shift()
38
39
  end
39
- File.truncate(Files.context_path, 0)
40
- tmp_arr.each { |line| File.open(Files.context_path, "a") { |file| file.write("#{line.to_json}\n") } }
41
- File.open(Files.context_path, "a") { |file| file.write("#{context.to_json}\n") }
40
+ File.truncate(context_path, 0)
41
+ tmp_arr.each { |line| File.open(context_path, "a") { |file| file.write("#{line.to_json}\n") } }
42
+ File.open(context_path, "a") { |file| file.write("#{context.to_json}\n") }
42
43
  end
43
44
 
44
45
  def self.delete_context()
45
- Logging.log("Deleting previous context.")
46
- File.truncate(Files.context_path, 0)
46
+ log("Deleting previous context.")
47
+ File.truncate(context_path, 0)
47
48
  end
48
49
 
49
50
  def self.save_context_file(file_path)
50
- puts "File path: #{file_path}"
51
51
  unless file_path.nil?
52
52
  file_in = File.open(file_path, 'r')
53
- file_out = File.open(Files.context_file_path, 'w')
53
+ file_out = File.open(context_file_path, 'w')
54
54
  char_count = 0
55
55
  file_in.each do |line|
56
56
  puts "Line: #{line}"
@@ -61,17 +61,17 @@ class Context
61
61
  file_out.close
62
62
 
63
63
  if char_count > 10000
64
- Logging.log("Warning: The file you are trying to feed to the API is #{char_count} characters long. This consumes a lot of tokens.")
64
+ log("Warning: The file you are trying to feed to the API is #{char_count} characters long. This consumes a lot of tokens.")
65
65
  end
66
66
  else
67
- Logging.log("No file path given.")
67
+ log("No file path given.")
68
68
  end
69
69
  rescue Errno::ENOENT
70
- Logging.log("No file at '#{file_path}' found.")
70
+ log("No file at '#{file_path}' found.")
71
71
  end
72
72
 
73
73
  def self.load_context_file()
74
- file = File.open(Files.context_file_path, 'r')
74
+ file = File.open(context_file_path, 'r')
75
75
  file_as_string = ""
76
76
  file.each do |line|
77
77
  file_as_string += line
@@ -79,8 +79,8 @@ class Context
79
79
 
80
80
  return file_as_string
81
81
  rescue Errno::ENOENT
82
- Logging.log("No file at '#{Files.context_file_path}' found.")
83
- Logging.log("Load a file with 'aa -lf <file_path>'")
82
+ log("No file at '#{context_file_path}' found.")
83
+ log("Load a file with 'aa -lf <file_path>'")
84
84
  return ""
85
85
  end
86
86
 
data/lib/files.rb CHANGED
@@ -1,21 +1,21 @@
1
1
  module Files
2
- def self.root
2
+ def root
3
3
  File.expand_path("./../", __dir__)
4
4
  end
5
5
 
6
- def self.file_path
6
+ def file_path
7
7
  File.expand_path("./../files/", __dir__)
8
8
  end
9
9
 
10
- def self.context_path
10
+ def context_path
11
11
  File.expand_path("./../files/context.jsonl", __dir__)
12
12
  end
13
13
 
14
- def self.config_path
14
+ def config_path
15
15
  File.expand_path("./../config/config.yml", __dir__)
16
16
  end
17
17
 
18
- def self.context_file_path
18
+ def context_file_path
19
19
  File.expand_path("./../files/context_file.txt", __dir__)
20
20
  end
21
21
  end
data/lib/help.rb CHANGED
@@ -1,85 +1,49 @@
1
1
  require 'rubygems'
2
2
 
3
- class Help
4
- def self.display_help()
5
- Logging.log("Usage: aa [options] [input]")
6
- Logging.log(" -lf, --loadfile <path>: Load file into context")
7
- Logging.log(" -f, --file: Read from context file")
8
- Logging.log(" -c, --conversation: Append to conversation (max 10 Questions / Answers pairs saved)")
9
- Logging.log(" -d, --delete: Delete conversation")
10
- Logging.log(" -i, --interactive: Interactive mode, always a conversation. Clear context with 'clear' (exit with 'exit' or 'quit')")
11
- Logging.log(" -w, --whisper <path>: Transcribe audio file")
12
- Logging.log(" -t, --translate <path>: Translate audio file")
13
- Logging.log("\n Options:")
14
- Logging.log(" --config: Edit config file")
15
- Logging.log(" -v, --version: Display version")
16
- Logging.log(" -h, --help: Display this help message")
17
-
18
- end
19
-
20
- def self.display_api_key()
21
- Logging.log("You need to set your API key in the file: ./config/config.yml")
22
- Logging.log("Create the file if it doesn't exist.")
23
- Logging.log("Add the following line to the file:")
24
- Logging.log(" OPENAI_API_KEY: <your API key>")
25
- Logging.log("You can get your API key from: https://openai.com/")
26
- end
27
-
28
- ## This don't work yet. Need to rework the way we handle args.
29
- def self.display_help_file()
30
- ## TODO: How to work with files
31
- end
32
3
 
33
- ## This don't work yet. Need to rework the way we handle args.
34
- def self.display_help_conversation()
35
- ## TODO: How to work with conversation
36
- end
4
+ class Help
5
+ extend Logging
37
6
 
38
7
  def self.display_version()
39
- spec = Gem::Specification::load("ask-ai.gemspec")
40
- Logging.log("Version: #{spec.version}")
41
- end
42
-
43
- def self.display_usage()
44
- Logging.log("Usage: ./main.rb [options] [input]")
45
-
46
- Logging.log("There are two types of options, flags and arguments.")
8
+ ## Without using gemspec
9
+ spec = Gem::Specification::find_by_name("ask-ai")
10
+ log("Version: ask-ai-#{spec.version}")
47
11
  end
48
12
 
49
13
  def self.interactive_help(command)
50
14
  case command
51
15
  when '-w'
52
- Logging.log("Ex: -w /home/name/sound_file.m4a")
53
- Logging.log("Will transcribe the audio file.")
16
+ log("Ex: -w /home/name/sound_file.m4a")
17
+ log("Will transcribe the audio file.")
54
18
  when '-t'
55
- Logging.log("Ex: -t /home/name/sound_file.m4a")
56
- Logging.log("Will translate the audio file to English.")
19
+ log("Ex: -t /home/name/sound_file.m4a")
20
+ log("Will translate the audio file to English.")
57
21
  when '-lf'
58
- Logging.log("Ex: -lf /home/name/some_text_file.txt'")
59
- Logging.log("Will load the file into context.")
60
- Logging.log("The file should a [txt, CSV]. More formats coming soon.")
22
+ log("Ex: -lf /home/name/some_text_file.txt'")
23
+ log("Will load the file into context.")
24
+ log("The file should a [txt, CSV]. More formats coming soon.")
61
25
  when '-f'
62
- Logging.log("Ex: -f Can you describe the file i provided?")
26
+ log("Ex: -f Can you describe the file i provided?")
63
27
  when 'config'
64
- Logging.log("Ex: config key <your API key>")
65
- Logging.log("Ex: config temp <0.0 - 1.0>")
66
- Logging.log("Ex: config context <0 - 100>")
67
- Logging.log("Beaware that the more context you use, the more expensive it will be.")
28
+ log("Ex: config key <your API key>")
29
+ log("Ex: config temp <0.0 - 1.0>")
30
+ log("Ex: config context <0 - 100>")
31
+ log("Beaware that the more context you use, the more expensive it will be.")
68
32
  else
69
- Logging.log("No help for: #{command}")
33
+ log("No help for: #{command}")
70
34
  end
71
35
 
72
36
  end
73
37
 
74
38
  def self.interactive_desc()
75
- Logging.log("Type 'exit' or 'quit' to exit.")
76
- Logging.log("Type 'clear' to clear context.")
77
- Logging.log("Type 'show' to show context.")
78
- Logging.log("Type 'help' to show help.")
79
- Logging.log("Type 'config [key, temp, context]' to change config.")
80
- Logging.log("Type '-w <filepath>' to whisper transcribe.")
81
- Logging.log("Type '-t' <filepath> to whisper translate.")
82
- Logging.log("Type '-lf' <filepath> to load file.")
83
- Logging.log("Type '-f' to use loaded file as context.")
39
+ log("Type 'exit' or 'quit' to exit.")
40
+ log("Type 'clear' to clear context.")
41
+ log("Type 'show' to show context.")
42
+ log("Type 'help' to show help.")
43
+ log("Type 'config [key, temp, context]' to change config.")
44
+ log("Type '-w <filepath>' to whisper transcribe.")
45
+ log("Type '-t' <filepath> to whisper translate.")
46
+ log("Type '-lf' <filepath> to load file.")
47
+ log("Type '-f' to use loaded file as context.")
84
48
  end
85
49
  end
data/lib/logging.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'logger'
2
2
 
3
3
  module Logging
4
- def self.log(msg)
4
+ def log(msg)
5
5
  logger = Logger.new(STDOUT)
6
6
  logger.formatter = proc { |_severity, _datetime, _progname, msg| "#{msg}\n" }
7
7
  logger.info(msg)
data/lib/main.rb CHANGED
@@ -6,192 +6,80 @@ require 'readline'
6
6
  require 'io/console'
7
7
  require_relative './logging.rb'
8
8
  require_relative './prompt.rb'
9
- require_relative './handle_args.rb'
10
9
  require_relative './help.rb'
11
10
  require_relative './context.rb'
12
11
  require_relative './files.rb'
13
12
  require_relative './config.rb'
14
13
 
15
14
  class Main
16
- include Logging, Files, Config
15
+ extend Logging, Files, Config
16
+ LIST = [
17
+ "exit", "quit", "version", "clear", "help", "show",
18
+ "-w", "-t", "-lf", "-f", "config", "temp", "context"
19
+ ].sort
17
20
 
18
21
  def self.run()
19
- config = load_env()
20
22
 
21
- ## This does not work. Need to fix this.
22
- if (config == false || config.nil?)
23
- Logging.log("No API key found.")
24
- Help.display_api_key()
25
- Logging.log('If you want to set your API key now, type (y)es and press enter.')
26
- while input = Readline.readline("> ", true) do
27
- if input.empty?
28
- Logging.log("Exiting.")
29
- exit
30
- elsif input == "y" || input == "yes" || input == "Y" || input == "Yes"
31
- set_key()
32
- break
33
- else
34
- Logging.log('Invalid input (y)es or press enter to exit.')
35
- end
36
- end
37
- end
23
+ ## When using Readline, TAB will auto-complete
24
+ ## But it will only auto-complete from the LIST
25
+ ## Need to handle directories and files when -lf, -w, -t are used
26
+ #comp = proc { |s| LIST.grep(/^#{Regexp.escape(s)}/) }
27
+ #Readline.completion_append_character = ""
28
+ #Readline.completion_proc = comp
38
29
 
39
- context = Context.load_context()
40
- options_and_input = HandleArgs.handle_args()
41
- options = options_and_input.select { |k, v| k.start_with?("option_") }
42
- input = options_and_input["input"]
43
-
44
- halt_options = ["-h", "--help", "-v", "--version", "--key"]
45
-
46
- ## Hack... Need to fix this.
47
- if options.empty?
48
- options = { "option_0" => "simple" }
49
- end
50
-
51
- options.each do |k, v|
52
- if halt_options.include?(v)
53
- ## Options that halt the program.
54
- case v
55
- when "-h", "--help"
56
- Help.display_help()
57
- exit
58
- when "-v", "--version"
59
- Help.display_version()
60
- exit
61
-
62
- when "--key"
63
- set_key(api_key: nil)
64
- else
65
- Help.display_help()
66
- exit
30
+ Help.interactive_desc()
31
+ while input = Readline.readline("\n> ", true) do
32
+ case input
33
+ when "exit", "quit"
34
+ break
35
+ when "version"
36
+ Help.display_version()
37
+ when "clear"
38
+ log("Clearing context...")
39
+ Context.delete_context()
40
+ when 'help'
41
+ Help.interactive_desc()
42
+ when /^help/
43
+ strip_input = input.sub(/^help/, "").strip
44
+ Help.interactive_help(strip_input)
45
+ when "show"
46
+ log("\n")
47
+ log(Context.load_context())
48
+ when /^-w/
49
+ stript_input = input.sub(/^-w/, "").strip
50
+ log(Prompt.whisper_transcribe(stript_input, interactive: true))
51
+ when /^-t/
52
+ stript_input = input.sub(/^-t/, "").strip
53
+ log(Prompt.whisper_translate(stript_input, interactive: true))
54
+ when /^-lf/
55
+ stript_input = input.sub(/^-lf/, "").strip
56
+ log("Loading File #{stript_input}")
57
+ Context.save_context_file(stript_input)
58
+ when /^-f/
59
+ stript_input = input.sub(/^-f/, "").strip
60
+ file_as_string = Context.load_context_file()
61
+ if file_as_string.empty?
62
+ log("No file loaded.")
63
+ next
67
64
  end
68
- else
69
- ## Options that don't halt the program.
70
- case v
71
- when "-f", "--file"
72
- file_as_string = Context.load_context_file()
73
- if file_as_string.empty?
74
- exit
75
- end
76
- Prompt.stream_prompt(input, file_as_string)
77
- when "-lf", "--loadfile"
78
- Logging.log("Loading File #{input}")
79
- Context.save_context_file(input)
80
- when "-d", "--delete"
81
- if input.nil?
82
- Context.delete_context()
83
- else
84
- Context.delete_context()
85
- Context.save_context(Prompt.stream_prompt(input, context))
86
- end
87
- when "-c", "--conversation"
88
- Logging.log(input)
89
- Context.save_context(Prompt.stream_prompt(input, context))
90
- when "-w", "--whisper"
91
- Logging.log(Prompt.whisper_transcribe(input))
92
- when "-t", "--translate"
93
- Logging.log(Prompt.whisper_translate(input))
94
- when "-i", "--interactive"
95
- Logging.log("Interactive mode...")
96
- Help.interactive_desc()
97
- while input = Readline.readline("\n> ", true) do
98
- case input
99
- when "exit", "quit"
100
- break
101
- when "clear"
102
- Logging.log("Clearing context...")
103
- Context.delete_context()
104
- when 'help'
105
- Help.interactive_desc()
106
- when /^help/
107
- strip_input = input.sub(/^help/, "").strip
108
- #TODO: This should be a specific help for interactive.
109
- Help.interactive_help(strip_input)
110
- when "show"
111
- Logging.log("\n")
112
- Logging.log(Context.load_context())
113
- when /^-w/
114
- stript_input = input.sub(/^-w/, "").strip
115
- Logging.log(Prompt.whisper_transcribe(stript_input, interactive: true))
116
- when /^-t/
117
- stript_input = input.sub(/^-t/, "").strip
118
- Logging.log(Prompt.whisper_translate(stript_input, interactive: true))
119
- when /^-lf/
120
- stript_input = input.sub(/^-lf/, "").strip
121
- Logging.log("Loading File #{stript_input}")
122
- Context.save_context_file(stript_input)
123
- when /^-f/
124
- stript_input = input.sub(/^-f/, "").strip
125
- file_as_string = Context.load_context_file()
126
- if file_as_string.empty?
127
- Logging.log("No file loaded.")
128
- next
129
- end
130
- Logging.log("")
131
- Prompt.stream_prompt(stript_input, file_as_string)
132
- Logging.log("")
133
- when ""
134
- Logging.log("No input given.")
135
- when /^config/
136
- strip_input = input.sub(/^config/, "").strip
137
- Config.set_config(strip_input)
138
- #set_key(api_key: nil)
139
- else
140
- #options_and_input = HandleArgs.handle_args()
141
- context = Context.load_context()
142
- Logging.log("")
143
- Context.save_context(Prompt.stream_prompt(input, context))
144
- Logging.log("")
145
- end
146
- end
147
- Logging.log("Exiting...")
148
- when "simple"
149
- if !input.nil?
150
- Prompt.stream_prompt(input)
151
- else
152
- Logging.log("No input given.")
153
- Help.display_help()
154
- end
155
- else
156
- Help.display_help()
157
- end
158
- end
159
- end
160
- end
161
-
162
- private
163
-
164
- def self.set_key(api_key: nil)
165
- if api_key.nil?
166
- Logging.log("Setting API key...")
167
- Logging.log("Enter API key: (or press enter to exit)")
168
-
169
- while input = Readline.readline("> ", true) do
170
- if input.empty?
171
- Logging.log("Exiting.")
172
- exit
173
- else
174
- api_key = input.strip
175
- break
65
+ log("")
66
+ Prompt.stream_prompt(stript_input, file_as_string)
67
+ log("")
68
+ when ""
69
+ log("No input given.")
70
+ when /^config/
71
+ strip_input = input.sub(/^config/, "").strip
72
+ res = set_config(strip_input)
73
+ if res.is_a?(String)
74
+ log(res)
176
75
  end
76
+ else
77
+ context = Context.load_context()
78
+ log("")
79
+ Context.save_context(Prompt.stream_prompt(input, context))
80
+ log("")
177
81
  end
178
- Logging.log("Saving API key...")
179
82
  end
180
-
181
- FileUtils.mkdir_p(File.dirname(Files.config_path))
182
- File.open(Files.config_path, "w") do |f|
183
- f.write(YAML.dump({ "OPENAI_API_KEY" => api_key }))
184
- end
185
- Logging.log("API key saved.")
186
- Logging.log("")
187
- end
188
-
189
- def self.load_env()
190
- #Config.load_key()
191
- YAML.load(File.read(Files.config_path))
192
-
193
- rescue Errno::ENOENT
194
- Logging.log("No config.yml found.")
195
83
  end
196
84
  end
197
85
 
data/lib/prompt.rb CHANGED
@@ -3,10 +3,9 @@ require_relative './files.rb'
3
3
  require_relative './config.rb'
4
4
 
5
5
  class Prompt
6
- include Files
7
- include Config
6
+ extend Files, Config, Logging
8
7
  ## Streams the response, VERY NICE
9
- def self.stream_prompt(input, conversation = '', temp = Config.load_temperature())
8
+ def self.stream_prompt(input, conversation = '', temp = load_temperature())
10
9
  if temp.nil?
11
10
  temp = 0.7
12
11
  end
@@ -16,23 +15,25 @@ class Prompt
16
15
  conversation += "\n My question: #{input}"
17
16
  end
18
17
  response = ''
19
- client.chat(
20
- parameters: {
21
- model: "gpt-3.5-turbo",
22
- messages: [{ role: "user", content: conversation}],
23
- temperature: temp, ## Should be a parameter
24
- stream: proc do |chunk, _bytesize|
25
- response += chunk.dig("choices", 0, "delta", "content") unless chunk.dig("choices", 0, "delta", "content").nil?
26
- print chunk.dig("choices", 0, "delta", "content")
27
- end
18
+ unless client.nil?
19
+ client.chat(
20
+ parameters: {
21
+ model: "gpt-3.5-turbo",
22
+ messages: [{ role: "user", content: conversation}],
23
+ temperature: temp, ## Should be a parameter
24
+ stream: proc do |chunk, _bytesize|
25
+ response += chunk.dig("choices", 0, "delta", "content") unless chunk.dig("choices", 0, "delta", "content").nil?
26
+ print chunk.dig("choices", 0, "delta", "content")
27
+ end
28
+ }
29
+ )
30
+ context = {
31
+ "input" => input,
32
+ "response" => response,
28
33
  }
29
- )
30
- context = {
31
- "input" => input,
32
- "response" => response,
33
- }
34
34
 
35
- return context
35
+ return context
36
+ end
36
37
  end
37
38
 
38
39
  ## Not implemented only scaffolding
@@ -47,7 +48,7 @@ class Prompt
47
48
 
48
49
  def self.whisper_translate(file_path, interactive = false)
49
50
  if (file_path.nil? || !file_path.end_with?(*['.mp3', '.wav', '.m4a', '.webm', '.mpeg', '.mpga']))
50
- Logging.log("No file given or wrong file type")
51
+ log("No file given or wrong file type")
51
52
  unless interactive
52
53
  exit
53
54
  end
@@ -59,27 +60,29 @@ class Prompt
59
60
  exit
60
61
  end
61
62
  else
62
- response = client.audio.translate(
63
- parameters: {
64
- model: "whisper-1",
65
- file: File.open(file_path, "rb"),
66
- })
67
- if (response["text"].nil? || response["text"].empty?)
68
- Logging.log("No text found")
69
- unless interactive
70
- exit
63
+ unless client.nil?
64
+ response = client.audio.translate(
65
+ parameters: {
66
+ model: "whisper-1",
67
+ file: File.open(file_path, "rb"),
68
+ })
69
+ if (response["text"].nil? || response["text"].empty?)
70
+ log("No text found")
71
+ unless interactive
72
+ exit
73
+ end
71
74
  end
75
+ return response["text"]
72
76
  end
73
- return response["text"]
74
77
  end
75
78
  end
76
79
  rescue Errno::ENOENT => e
77
- Logging.log(e)
80
+ log(e)
78
81
  end
79
82
 
80
83
  def self.whisper_transcribe(file_path, interactive = false)
81
84
  if (file_path.nil? || !file_path.end_with?(*['.mp3', '.wav', '.m4a', '.webm', '.mpeg', '.mpga']))
82
- Logging.log("No file given")
85
+ log("No file given")
83
86
  unless interactive
84
87
  exit
85
88
  end
@@ -91,31 +94,40 @@ class Prompt
91
94
  exit
92
95
  end
93
96
  else
94
- response = client.audio.transcribe(
95
- parameters: {
96
- model: "whisper-1",
97
- file: File.open(file_path, "rb"),
98
- })
99
- if (response["text"].nil? || response["text"].empty?)
100
- Logging.log("No text found")
101
- unless interactive
102
- exit
97
+ unless client.nil?
98
+ response = client.audio.transcribe(
99
+ parameters: {
100
+ model: "whisper-1",
101
+ file: File.open(file_path, "rb"),
102
+ })
103
+ if (response["text"].nil? || response["text"].empty?)
104
+ log("No text found")
105
+ unless interactive
106
+ exit
107
+ end
103
108
  end
109
+ return response["text"]
104
110
  end
105
- return response["text"]
106
111
  end
107
112
  end
108
113
  rescue Errno::ENOENT => e
109
114
  #Logging.log("File not found")
110
- Logging.log(e)
115
+ log(e)
111
116
  end
112
117
 
113
118
  private
114
119
 
115
120
  def self.client()
116
- conf = YAML.load(File.read(Files.config_path))
117
- key = conf["OPENAI_API_KEY"]
121
+ conf = YAML.load(File.read(config_path))
122
+ unless conf == false
123
+ key = conf["OPENAI_API_KEY"]
124
+ end
118
125
 
119
- OpenAI::Client.new(access_token: key)
126
+ begin
127
+ OpenAI::Client.new(access_token: key)
128
+ rescue OpenAI::ConfigurationError => e
129
+ log("OpenAI API key not found, run 'config key' to set it")
130
+ return nil
131
+ end
120
132
  end
121
133
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ask-ai
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oskar Franck
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-04 00:00:00.000000000 Z
11
+ date: 2023-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-openai
@@ -39,7 +39,6 @@ files:
39
39
  - lib/config.rb
40
40
  - lib/context.rb
41
41
  - lib/files.rb
42
- - lib/handle_args.rb
43
42
  - lib/help.rb
44
43
  - lib/logging.rb
45
44
  - lib/main.rb
@@ -63,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
62
  - !ruby/object:Gem::Version
64
63
  version: '0'
65
64
  requirements: []
66
- rubygems_version: 3.3.7
65
+ rubygems_version: 3.2.33
67
66
  signing_key:
68
67
  specification_version: 4
69
68
  summary: A simple CLI for OpenAI's GPT-3 API.
data/lib/handle_args.rb DELETED
@@ -1,61 +0,0 @@
1
- class HandleArgs
2
- def self.permitted_options()
3
- ## TODO: Add more options.
4
- ## -t --translate
5
- ## -temp --temperature (need to handle value after input)
6
-
7
- [
8
- '-h',
9
- '--help',
10
- '-v',
11
- '--version',
12
- '-f',
13
- '--file',
14
- '-lf',
15
- '--loadfile',
16
- '-d',
17
- '--delete',
18
- '-c',
19
- '--conversation',
20
- '--finetune',
21
- '-w', '--whisper',
22
- '-t', '--translate',
23
- '-i', '--interactive',
24
- '--key',
25
- ]
26
- end
27
-
28
- def self.handle_args()
29
- p_args = HandleArgs.permitted_options()
30
- ## This is the hash that will be returned.
31
- args_hash = {}
32
-
33
- ## This handles args then called as ruby script.
34
- ARGV.each_with_index do |arg, i|
35
- #Logging.log("Arg: #{arg}")
36
-
37
- if arg.start_with?('-')
38
- ## This is an option.
39
- if p_args.include?(arg)
40
- ## This is a permitted / available option.
41
- #Logging.log("Option: #{arg}")
42
- args_hash["option_#{i}"] = arg
43
- else
44
- ## This is an unknown option.
45
- ## TODO: Handle unknown option. display help? discard?
46
- Logging.log("Unknown option: #{arg}")
47
- args_hash["option_#{i}"] = arg
48
- end
49
- else
50
- ## This is input.
51
- ## This if statement append all 'input' args to one string.
52
- if args_hash["input"].nil?
53
- args_hash["input"] = arg
54
- else
55
- args_hash["input"] = "#{args_hash['input']} #{arg}"
56
- end
57
- end
58
- end
59
- return args_hash
60
- end
61
- end