ask-ai 0.0.2 → 0.0.4

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: a48ff0dd24e51f3da64aaa2470e87320a644b57ab3474d42ea72031724ba62e6
4
- data.tar.gz: d5b12d50967f08cfd7619cc0d0c289df220bf6de29e9d1946a5c14e6bc31753e
3
+ metadata.gz: b9aac9c44ecfb8fd4a68b1329f7dc48e37b09910011b7a1d1ad085f2afd48407
4
+ data.tar.gz: c3b58592fce1e23ae7184a3b6d37d58451b8da82992c90dd503c4705a1fb94ff
5
5
  SHA512:
6
- metadata.gz: b5b3cfeecfbc3082c3e406c9d89f80d2129e61e24c15f5bcaf0e2309af1e66b272f8d3b153019a5126895ab8df2b6da106870fdb1d50e27255e3b6225d01dc5a
7
- data.tar.gz: 7aac19c35dec0c213bd00fbe6176003582d2d0abef571926171bcba0b0a3514c8116924bd628f1a677c8d0f78b1f4df128958492292bee3571474a9b018101c9
6
+ metadata.gz: f075fc93f55659c6f8d9331f5e508dcc6ac9f77e52cf4a8e2df2d25e3e727a8bef5f8f253588a4de06b8c67f7f2704792ed62f7c7e98d4337b5df0b843f34652
7
+ data.tar.gz: 4d74731c56a046231dc11bd889c37c42e591dfcaebef7e1aaba11d6a0dd64c4942e01474e67f526b2a061a1681d806befa9934f60c070d2b0ead4fa45bc75d8e
data/config/config.yml CHANGED
@@ -1,2 +0,0 @@
1
- OPENAI_API_KEY: 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
2
-
data/lib/context.rb CHANGED
@@ -1,7 +1,10 @@
1
+ require_relative './files.rb'
2
+
1
3
  class Context
4
+ include Files
2
5
  def self.load_context()
3
- if File.exist?(CONTEXT_PATH)
4
- conversation = File.readlines(CONTEXT_PATH).map { |line| JSON.parse(line) }
6
+ if File.exist?(Files.context_path)
7
+ conversation = File.readlines(Files.context_path).map { |line| JSON.parse(line) }
5
8
  else
6
9
  conversation = []
7
10
  end
@@ -25,27 +28,27 @@ class Context
25
28
  ## Max 10 previous Q / A to save tokens.
26
29
  def self.save_context(context)
27
30
  tmp_arr = []
28
- unless File.exist?(CONTEXT_PATH)
29
- File.open(CONTEXT_PATH, "w") {}
31
+ unless File.exist?(Files.context_path)
32
+ File.open(Files.context_path, "w") {}
30
33
  end
31
- File.readlines(CONTEXT_PATH).map { |line| tmp_arr.push(JSON.parse(line)) }
34
+ File.readlines(Files.context_path).map { |line| tmp_arr.push(JSON.parse(line)) }
32
35
  if tmp_arr.length > 9
33
36
  tmp_arr.shift()
34
37
  end
35
- File.truncate(CONTEXT_PATH, 0)
36
- tmp_arr.each { |line| File.open(CONTEXT_PATH, "a") { |file| file.write("#{line.to_json}\n") } }
37
- File.open(CONTEXT_PATH, "a") { |file| file.write("#{context.to_json}\n") }
38
+ File.truncate(Files.context_path, 0)
39
+ tmp_arr.each { |line| File.open(Files.context_path, "a") { |file| file.write("#{line.to_json}\n") } }
40
+ File.open(Files.context_path, "a") { |file| file.write("#{context.to_json}\n") }
38
41
  end
39
42
 
40
43
  def self.delete_context()
41
- puts "Deleting previous context."
42
- File.truncate(CONTEXT_PATH, 0)
44
+ Logging.log("Deleting previous context.")
45
+ File.truncate(Files.context_path, 0)
43
46
  end
44
47
 
45
48
  def self.save_context_file(file_path)
46
49
  unless file_path.nil?
47
50
  file_in = File.open(file_path, 'r')
48
- file_out = File.open(CONTEXT_FILE_PATH, 'w')
51
+ file_out = File.open(Files.context_file_path, 'w')
49
52
  char_count = 0
50
53
  file_in.each do |line|
51
54
  char_count += line.length
@@ -53,17 +56,17 @@ class Context
53
56
  end
54
57
 
55
58
  if char_count > 10000
56
- puts "Warning: The file you are trying to feed to the API is #{char_count} characters long. This consumes a lot of tokens."
59
+ Logging.log("Warning: The file you are trying to feed to the API is #{char_count} characters long. This consumes a lot of tokens.")
57
60
  end
58
61
  else
59
- puts "No file path given."
62
+ Logging.log("No file path given.")
60
63
  end
61
64
  rescue Errno::ENOENT
62
- puts "No file at '#{file_path}' found."
65
+ Logging.log("No file at '#{file_path}' found.")
63
66
  end
64
67
 
65
68
  def self.load_context_file()
66
- file = File.open(CONTEXT_FILE_PATH, 'r')
69
+ file = File.open(Files.context_file_path, 'r')
67
70
  file_as_string = ""
68
71
  file.each do |line|
69
72
  file_as_string += line
@@ -71,8 +74,8 @@ class Context
71
74
 
72
75
  return file_as_string
73
76
  rescue Errno::ENOENT
74
- puts "No file at '#{CONTEXT_FILE_PATH}' found."
75
- puts "Load a file with 'aa -lf <file_path>'"
77
+ Logging.log("No file at '#{Files.context_file_path}' found.")
78
+ Logging.log("Load a file with 'aa -lf <file_path>'")
76
79
  return ""
77
80
  end
78
81
 
data/lib/files.rb ADDED
@@ -0,0 +1,21 @@
1
+ module Files
2
+ def self.root
3
+ File.expand_path("./../", __dir__)
4
+ end
5
+
6
+ def self.file_path
7
+ File.expand_path("./../files/", __dir__)
8
+ end
9
+
10
+ def self.context_path
11
+ File.expand_path("./../files/context.jsonl", __dir__)
12
+ end
13
+
14
+ def self.config_path
15
+ File.expand_path("./../config/config.yml", __dir__)
16
+ end
17
+
18
+ def self.context_file_path
19
+ File.expand_path("./../files/context_file.txt", __dir__)
20
+ end
21
+ end
data/lib/handle_args.rb CHANGED
@@ -1,4 +1,4 @@
1
- class HandleArgs
1
+ class HandleArgs
2
2
  def self.permitted_options()
3
3
  ## TODO: Add more options.
4
4
  ## -t --translate
@@ -32,18 +32,18 @@ end
32
32
 
33
33
  ## This handles args then called as ruby script.
34
34
  ARGV.each_with_index do |arg, i|
35
- #puts "Arg: #{arg}"
35
+ #Logging.log("Arg: #{arg}")
36
36
 
37
37
  if arg.start_with?('-')
38
38
  ## This is an option.
39
39
  if p_args.include?(arg)
40
40
  ## This is a permitted / available option.
41
- #puts "Option: #{arg}"
41
+ #Logging.log("Option: #{arg}")
42
42
  args_hash["option_#{i}"] = arg
43
43
  else
44
44
  ## This is an unknown option.
45
45
  ## TODO: Handle unknown option. display help? discard?
46
- puts "Unknown option: #{arg}"
46
+ Logging.log("Unknown option: #{arg}")
47
47
  args_hash["option_#{i}"] = arg
48
48
  end
49
49
  else
data/lib/help.rb CHANGED
@@ -2,27 +2,27 @@ require 'rubygems'
2
2
 
3
3
  class Help
4
4
  def self.display_help()
5
- puts "Usage: aa [options] [input]"
6
- puts " -lf, --loadfile <path>: Load file into context"
7
- puts " -f, --file: Read from context file"
8
- puts " -c, --conversation: Append to conversation (max 10 Questions / Answers pairs saved)"
9
- puts " -d, --delete: Delete conversation"
10
- puts " -i, --interactive: Interactive mode, always a conversation. Clear context with 'clear' (exit with 'exit' or 'quit')"
11
- puts " -w, --whisper <path>: Transcribe audio file"
12
- puts " -t, --translate <path>: Translate audio file"
13
- puts "\n Options:"
14
- puts " --config: Edit config file"
15
- puts " -v, --version: Display version"
16
- puts " -h, --help: Display this help message"
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
17
 
18
18
  end
19
19
 
20
20
  def self.display_api_key()
21
- puts "You need to set your API key in the file: ./config/config.yml"
22
- puts "Create the file if it doesn't exist."
23
- puts "Add the following line to the file:"
24
- puts " OPENAI_API_KEY: <your API key>"
25
- puts "You can get your API key from: https://openai.com/"
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
26
  end
27
27
 
28
28
  ## This don't work yet. Need to rework the way we handle args.
@@ -36,13 +36,13 @@ class Help
36
36
  end
37
37
 
38
38
  def self.display_version()
39
- spec = Gem::Specification::load("aa.gemspec")
40
- puts "Version: #{spec.version}"
39
+ spec = Gem::Specification::load("ask-ai.gemspec")
40
+ Logging.log("Version: #{spec.version}")
41
41
  end
42
42
 
43
43
  def self.display_usage()
44
- puts "Usage: ./main.rb [options] [input]"
44
+ Logging.log("Usage: ./main.rb [options] [input]")
45
45
 
46
- puts "There are two types of options, flags and arguments."
46
+ Logging.log("There are two types of options, flags and arguments.")
47
47
  end
48
48
  end
data/lib/logging.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'logger'
2
+
3
+ module Logging
4
+ def self.log(msg)
5
+ logger = Logger.new(STDOUT)
6
+ logger.formatter = proc { |_severity, _datetime, _progname, msg| "#{msg}\n" }
7
+ logger.info(msg)
8
+ end
9
+ end
data/lib/main.rb CHANGED
@@ -4,39 +4,33 @@ require 'fileutils'
4
4
  require 'yaml'
5
5
  require 'readline'
6
6
  require 'io/console'
7
- [
8
- './prompt.rb',
9
- './handle_args.rb',
10
- './help.rb',
11
- './context.rb'
12
- ].each { |f| require_relative f }
7
+ require_relative './logging.rb'
8
+ require_relative './prompt.rb'
9
+ require_relative './handle_args.rb'
10
+ require_relative './help.rb'
11
+ require_relative './context.rb'
12
+ require_relative './files.rb'
13
13
 
14
- CONTEXT_PATH = File.expand_path("./../files/context.jsonl", __dir__)
15
- FILE_PATH = File.expand_path("./../files/", __dir__)
16
- CONFIG_PATH = File.expand_path("./../config/config.yml", __dir__)
17
- CONTEXT_FILE_PATH = File.expand_path("./../files/context_file.txt", __dir__)
18
14
  class Main
19
-
15
+ include Logging
16
+ include Files
20
17
  def self.run()
21
18
  config = load_env()
22
-
19
+
23
20
  ## This does not work. Need to fix this.
24
- if config.nil? || config['OPENAI_API_KEY'].nil?
25
- puts "No API key found."
21
+ if config == false
22
+ Logging.log("No API key found.")
26
23
  Help.display_api_key()
27
- puts 'If you want to set your API key now, type (y)es and press enter.'
28
- #puts "If you have an API key you can set it here."
29
- #puts "Enter API key: (or press enter to exit)"
30
-
24
+ Logging.log('If you want to set your API key now, type (y)es and press enter.')
31
25
  while input = Readline.readline("> ", true) do
32
26
  if input.empty?
33
- puts "Exiting."
27
+ Logging.log("Exiting.")
34
28
  exit
35
29
  elsif input == "y" || input == "yes" || input == "Y" || input == "Yes"
36
30
  set_key()
37
31
  exit
38
32
  else
39
- puts 'Invalid input (y)es or press enter to exit.'
33
+ Logging.log('Invalid input (y)es or press enter to exit.')
40
34
  end
41
35
  end
42
36
  end
@@ -80,7 +74,7 @@ class Main
80
74
  end
81
75
  Prompt.stream_prompt(input, file_as_string)
82
76
  when "-lf", "--loadfile"
83
- puts "Loading File #{input}"
77
+ Logging.log("Loading File #{input}")
84
78
  Context.save_context_file(input)
85
79
  when "-d", "--delete"
86
80
  if input.nil?
@@ -90,39 +84,57 @@ class Main
90
84
  Context.save_context(Prompt.stream_prompt(input, context))
91
85
  end
92
86
  when "-c", "--conversation"
93
- puts input
87
+ Logging.log(input)
94
88
  Context.save_context(Prompt.stream_prompt(input, context))
95
89
  when "-w", "--whisper"
96
- puts Prompt.whisper_transcribe(input)
90
+ Logging.log(Prompt.whisper_transcribe(input))
97
91
  when "-t", "--translate"
98
- puts Prompt.whisper_translate(input)
92
+ Logging.log(Prompt.whisper_translate(input))
99
93
  when "-i", "--interactive"
100
- puts "Interactive mode..."
101
- puts "Type 'exit' or 'quit' to exit."
102
- puts "Type 'clear' to clear context."
94
+ Logging.log("Interactive mode...")
95
+ Logging.log("Type 'exit' or 'quit' to exit.")
96
+ Logging.log("Type 'clear' to clear context.")
97
+ Logging.log("Type 'show' to show context.")
98
+ Logging.log("Type 'help' to show help.")
99
+ Logging.log("Type 'config' to change config.")
100
+ Logging.log("Type '-w' to whisper transcribe.")
101
+ Logging.log("Type '-t' to whisper translate.")
103
102
 
104
103
  while input = Readline.readline("\n> ", true) do
105
- if (input == "exit" || input == "quit")
106
- break
104
+ case input
105
+ when "exit", "quit"
106
+ break
107
+ when "clear"
108
+ Logging.log("Clearing context...")
109
+ Context.delete_context()
110
+ when "help"
111
+ #TODO: This should be a specific help for interactive.
112
+ Help.display_help()
113
+ when "show"
114
+ Logging.log("\n")
115
+ Logging.log(Context.load_context())
116
+ when /^-w/
117
+ stript_input = input.sub(/^-w/, "").strip
118
+ Logging.log(Prompt.whisper_transcribe(stript_input, interactive: true))
119
+ when /^-t/
120
+ stript_input = input.sub(/^-t/, "").strip
121
+ Logging.log(Prompt.whisper_translate(stript_input, interactive: true))
122
+ when "config"
123
+ set_key(api_key: nil)
124
+ else
125
+ #options_and_input = HandleArgs.handle_args()
126
+ context = Context.load_context()
127
+ Logging.log("")
128
+ Context.save_context(Prompt.stream_prompt(input, context))
129
+ Logging.log("")
107
130
  end
108
- if input == "clear"
109
- puts "Clearing context..."
110
- Context.delete_context()
111
- next
112
- end
113
- options_and_input = HandleArgs.handle_args()
114
- context = Context.load_context()
115
- puts "\n"
116
- Context.save_context(Prompt.stream_prompt(input, context))
117
- puts "\n"
118
131
  end
119
- puts "Exiting..."
120
- #Context.delete_context()
132
+ Logging.log("Exiting...")
121
133
  when "simple"
122
134
  if !input.nil?
123
135
  Prompt.stream_prompt(input)
124
136
  else
125
- puts "No input given."
137
+ Logging.log("No input given.")
126
138
  Help.display_help()
127
139
  end
128
140
  else
@@ -136,33 +148,33 @@ class Main
136
148
 
137
149
  def self.set_key(api_key: nil)
138
150
  if api_key.nil?
139
- puts "Setting API key..."
140
- puts "Enter API key: (or press enter to exit)"
151
+ Logging.log("Setting API key...")
152
+ Logging.log("Enter API key: (or press enter to exit)")
141
153
 
142
154
  while input = Readline.readline("> ", true) do
143
155
  if input.empty?
144
- puts "Exiting."
156
+ Logging.log("Exiting.")
145
157
  exit
146
158
  else
147
159
  api_key = input.strip
148
160
  break
149
161
  end
150
162
  end
151
- puts "Saving API key..."
163
+ Logging.log("Saving API key...")
152
164
  end
153
165
 
154
- FileUtils.mkdir_p(File.dirname(CONFIG_PATH))
155
- File.open(CONFIG_PATH, "w") do |f|
166
+ FileUtils.mkdir_p(File.dirname(Files.config_path))
167
+ File.open(Files.config_path, "w") do |f|
156
168
  f.write(YAML.dump({ "OPENAI_API_KEY" => api_key }))
157
169
  end
158
- puts "API key saved."
170
+ Logging.log("API key saved.")
159
171
  end
160
172
 
161
173
  def self.load_env()
162
- YAML.load(File.read(CONFIG_PATH))
174
+ YAML.load(File.read(Files.config_path))
163
175
 
164
176
  rescue Errno::ENOENT
165
- puts "No config.yml found."
177
+ Logging.log("No config.yml found.")
166
178
  end
167
179
  end
168
180
 
data/lib/paths.rb ADDED
@@ -0,0 +1,26 @@
1
+
2
+ module Files
3
+ def self.root
4
+ File.expand_path("./../", __dir__)
5
+ end
6
+
7
+ def self.context
8
+ File.expand_path("./../files/context.jsonl", __dir__)
9
+ end
10
+
11
+ def self.files
12
+ FILE_PATH
13
+ end
14
+
15
+ def self.config
16
+ CONFIG_PATH
17
+ end
18
+
19
+ def self.context_file
20
+ CONTEXT_FILE_PATH
21
+ end
22
+ end
23
+ CONTEXT_PATH = File.expand_path("./../files/context.jsonl", __dir__)
24
+ FILE_PATH = File.expand_path("./../files/", __dir__)
25
+ CONFIG_PATH = File.expand_path("./../config/config.yml", __dir__)
26
+ CONTEXT_FILE_PATH = File.expand_path("./../files/context_file.txt", __dir__)
data/lib/prompt.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  require "openai"
2
+ require_relative './files.rb'
2
3
 
3
4
  class Prompt
4
-
5
+ include Files
5
6
  ## Streams the response, VERY NICE
6
7
  def self.stream_prompt(input, conversation = '', temp = 0.7)
7
8
  if conversation.length == 0
@@ -9,7 +10,6 @@ class Prompt
9
10
  else
10
11
  conversation += "\n My question: #{input}"
11
12
  end
12
-
13
13
  response = ''
14
14
  client.chat(
15
15
  parameters: {
@@ -26,6 +26,7 @@ class Prompt
26
26
  "input" => input,
27
27
  "response" => response,
28
28
  }
29
+
29
30
  return context
30
31
  end
31
32
 
@@ -39,15 +40,19 @@ class Prompt
39
40
  client.files.delete(id: "file-123")
40
41
  end
41
42
 
42
- def self.whisper_translate(file_path)
43
+ def self.whisper_translate(file_path, interactive = false)
43
44
  if (file_path.nil? || !file_path.end_with?(*['.mp3', '.wav', '.m4a', '.webm', '.mpeg', '.mpga']))
44
- puts "No file given or wrong file type"
45
- exit
45
+ Logging.log("No file given or wrong file type")
46
+ unless interactive
47
+ exit
48
+ end
46
49
  else
47
50
  size = File.size(file_path).to_f / 2**20
48
51
  if size > 24
49
52
  warning("The file is above the maximum size of 25MB")
50
- exit
53
+ unless interactive
54
+ exit
55
+ end
51
56
  else
52
57
  response = client.audio.translate(
53
58
  parameters: {
@@ -55,25 +60,31 @@ class Prompt
55
60
  file: File.open(file_path, "rb"),
56
61
  })
57
62
  if (response["text"].nil? || response["text"].empty?)
58
- puts "No text found"
59
- exit
63
+ Logging.log("No text found")
64
+ unless interactive
65
+ exit
66
+ end
60
67
  end
61
- puts response["text"]
68
+ return response["text"]
62
69
  end
63
70
  end
64
71
  rescue Errno::ENOENT => e
65
- puts "File not found"
72
+ Logging.log(e)
66
73
  end
67
74
 
68
- def self.whisper_transcribe(file_path)
75
+ def self.whisper_transcribe(file_path, interactive = false)
69
76
  if (file_path.nil? || !file_path.end_with?(*['.mp3', '.wav', '.m4a', '.webm', '.mpeg', '.mpga']))
70
- puts "No file given"
71
- exit
77
+ Logging.log("No file given")
78
+ unless interactive
79
+ exit
80
+ end
72
81
  else
73
82
  size = File.size(file_path).to_f / 2**20
74
83
  if size > 24
75
- warning("The file is above the maximum size of 25MB, this may take")
76
- exit
84
+ warning("The file is above the maximum size of 25MB")
85
+ unless interactive
86
+ exit
87
+ end
77
88
  else
78
89
  response = client.audio.transcribe(
79
90
  parameters: {
@@ -81,20 +92,23 @@ class Prompt
81
92
  file: File.open(file_path, "rb"),
82
93
  })
83
94
  if (response["text"].nil? || response["text"].empty?)
84
- puts "No text found"
85
- exit
95
+ Logging.log("No text found")
96
+ unless interactive
97
+ exit
98
+ end
86
99
  end
87
- puts response["text"]
100
+ return response["text"]
88
101
  end
89
102
  end
90
103
  rescue Errno::ENOENT => e
91
- puts "File not found"
104
+ #Logging.log("File not found")
105
+ Logging.log(e)
92
106
  end
93
107
 
94
108
  private
95
109
 
96
110
  def self.client()
97
- conf = YAML.load(File.read(CONFIG_PATH))
111
+ conf = YAML.load(File.read(Files.config_path))
98
112
  key = conf["OPENAI_API_KEY"]
99
113
 
100
114
  OpenAI::Client.new(access_token: key)
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.2
4
+ version: 0.0.4
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-09-28 00:00:00.000000000 Z
11
+ date: 2023-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-openai
@@ -37,9 +37,12 @@ files:
37
37
  - files/context.jsonl
38
38
  - files/context_file.txt
39
39
  - lib/context.rb
40
+ - lib/files.rb
40
41
  - lib/handle_args.rb
41
42
  - lib/help.rb
43
+ - lib/logging.rb
42
44
  - lib/main.rb
45
+ - lib/paths.rb
43
46
  - lib/prompt.rb
44
47
  homepage: https://github.com/OskarFranck/terminal_chat
45
48
  licenses: