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 +4 -4
- data/config/config.yml +0 -3
- data/lib/config.rb +76 -31
- data/lib/context.rb +20 -20
- data/lib/files.rb +5 -5
- data/lib/help.rb +27 -63
- data/lib/logging.rb +1 -1
- data/lib/main.rb +60 -172
- data/lib/prompt.rb +57 -45
- metadata +3 -4
- data/lib/handle_args.rb +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d0033fda00a5a8399a6e9b71c75338b02269df54edb791bd82215e939492d15
|
4
|
+
data.tar.gz: 391d4cc9fe240289358f2489b61224b8d0706254174f9ad4a5ec7a109b7b3a89
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f749624e465e35a8ebcae8b7b9b312d1f4ffdbd8e390043a0fc87dae5eaf680931975eb87369eba855043eff52206c2daf47af95fc5136e7fb0134520275a28
|
7
|
+
data.tar.gz: 81d6f56f465808352087d5f53bab44b7a1af2d63292dbba4b5fab7f91b44fd3d78c6a591718d142f6348c6ab78bae8a54d84b2cba73e2164efd32dd3b27d4e1d
|
data/config/config.yml
CHANGED
data/lib/config.rb
CHANGED
@@ -1,65 +1,110 @@
|
|
1
1
|
require_relative './files.rb'
|
2
2
|
|
3
3
|
module Config
|
4
|
-
|
5
|
-
def
|
6
|
-
config = YAML.load_file(
|
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
|
11
|
-
config = YAML.load_file(
|
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(
|
16
|
+
File.open(config_path, 'w') { |f| YAML.dump(config, f) }
|
14
17
|
end
|
15
18
|
|
16
|
-
def
|
17
|
-
config = YAML.load_file(
|
18
|
-
config
|
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
|
22
|
-
config = YAML.load_file(
|
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(
|
32
|
+
File.open(config_path, 'w') { |f| YAML.dump(config, f) }
|
25
33
|
end
|
26
34
|
|
27
|
-
def
|
28
|
-
config = YAML.load_file(
|
29
|
-
config
|
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
|
33
|
-
config = YAML.load_file(
|
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(
|
48
|
+
File.open(config_path, 'w') { |f| YAML.dump(config, f) }
|
36
49
|
end
|
37
50
|
|
38
|
-
def
|
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
|
-
|
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.
|
50
|
-
|
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.
|
57
|
-
|
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
|
-
|
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
|
-
|
4
|
+
extend Files, Config, Logging
|
5
5
|
def self.load_context()
|
6
|
-
if File.exist?(
|
7
|
-
conversation = File.readlines(
|
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?(
|
32
|
-
File.open(
|
32
|
+
unless File.exist?(context_path)
|
33
|
+
File.open(context_path, "w") {}
|
33
34
|
end
|
34
|
-
File.readlines(
|
35
|
-
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(
|
40
|
-
tmp_arr.each { |line| File.open(
|
41
|
-
File.open(
|
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
|
-
|
46
|
-
File.truncate(
|
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(
|
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
|
-
|
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
|
-
|
67
|
+
log("No file path given.")
|
68
68
|
end
|
69
69
|
rescue Errno::ENOENT
|
70
|
-
|
70
|
+
log("No file at '#{file_path}' found.")
|
71
71
|
end
|
72
72
|
|
73
73
|
def self.load_context_file()
|
74
|
-
file = File.open(
|
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
|
-
|
83
|
-
|
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
|
2
|
+
def root
|
3
3
|
File.expand_path("./../", __dir__)
|
4
4
|
end
|
5
5
|
|
6
|
-
def
|
6
|
+
def file_path
|
7
7
|
File.expand_path("./../files/", __dir__)
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
10
|
+
def context_path
|
11
11
|
File.expand_path("./../files/context.jsonl", __dir__)
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
14
|
+
def config_path
|
15
15
|
File.expand_path("./../config/config.yml", __dir__)
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
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
|
-
|
34
|
-
|
35
|
-
## TODO: How to work with conversation
|
36
|
-
end
|
4
|
+
class Help
|
5
|
+
extend Logging
|
37
6
|
|
38
7
|
def self.display_version()
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
53
|
-
|
16
|
+
log("Ex: -w /home/name/sound_file.m4a")
|
17
|
+
log("Will transcribe the audio file.")
|
54
18
|
when '-t'
|
55
|
-
|
56
|
-
|
19
|
+
log("Ex: -t /home/name/sound_file.m4a")
|
20
|
+
log("Will translate the audio file to English.")
|
57
21
|
when '-lf'
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
26
|
+
log("Ex: -f Can you describe the file i provided?")
|
63
27
|
when 'config'
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
33
|
+
log("No help for: #{command}")
|
70
34
|
end
|
71
35
|
|
72
36
|
end
|
73
37
|
|
74
38
|
def self.interactive_desc()
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
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
|
-
|
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
|
-
##
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
7
|
-
include Config
|
6
|
+
extend Files, Config, Logging
|
8
7
|
## Streams the response, VERY NICE
|
9
|
-
def self.stream_prompt(input, conversation = '', temp =
|
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.
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
115
|
+
log(e)
|
111
116
|
end
|
112
117
|
|
113
118
|
private
|
114
119
|
|
115
120
|
def self.client()
|
116
|
-
conf = YAML.load(File.read(
|
117
|
-
|
121
|
+
conf = YAML.load(File.read(config_path))
|
122
|
+
unless conf == false
|
123
|
+
key = conf["OPENAI_API_KEY"]
|
124
|
+
end
|
118
125
|
|
119
|
-
|
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
|
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-
|
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.
|
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
|