monadic-chat 0.2.2 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -1
- data/README.md +90 -114
- data/apps/chat/chat.json +5 -1
- data/apps/chat/chat.md +4 -5
- data/apps/chat/chat.rb +13 -18
- data/apps/code/code.md +2 -4
- data/apps/code/code.rb +13 -18
- data/apps/linguistic/linguistic.json +1 -1
- data/apps/linguistic/linguistic.md +2 -4
- data/apps/linguistic/linguistic.rb +13 -18
- data/apps/novel/novel.md +2 -4
- data/apps/novel/novel.rb +13 -18
- data/apps/translate/translate.md +2 -4
- data/apps/translate/translate.rb +13 -18
- data/lib/monadic_app.rb +7 -22
- data/lib/monadic_chat/formatting.rb +1 -4
- data/lib/monadic_chat/interaction.rb +12 -12
- data/lib/monadic_chat/internals.rb +35 -181
- data/lib/monadic_chat/menu.rb +8 -8
- data/lib/monadic_chat/open_ai.rb +25 -14
- data/lib/monadic_chat/parameters.rb +12 -10
- data/lib/monadic_chat/version.rb +1 -1
- data/lib/monadic_chat.rb +43 -13
- data/monadic_chat.gemspec +1 -0
- metadata +16 -4
- data/doc/img/extra-template-json.png +0 -0
- data/doc/img/langacker-2001.svg +0 -41
@@ -39,35 +39,15 @@ class MonadicApp
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def wait
|
43
|
-
return self if @threads.empty?
|
44
|
-
|
45
|
-
print TTY::Cursor.save
|
46
|
-
message = PASTEL.red "Processing contextual data #{SPINNER} "
|
47
|
-
print message
|
48
|
-
|
49
|
-
TIMEOUT_SEC.times do |i|
|
50
|
-
raise MonadicError, "Error: something went wrong" if i + 1 == TIMEOUT_SEC
|
51
|
-
|
52
|
-
break if @threads.empty?
|
53
|
-
|
54
|
-
sleep 1
|
55
|
-
end
|
56
|
-
print TTY::Cursor.restore
|
57
|
-
print TTY::Cursor.clear_char(message.size)
|
58
|
-
|
59
|
-
self
|
60
|
-
end
|
61
|
-
|
62
42
|
def objectify
|
63
|
-
case @
|
64
|
-
when
|
65
|
-
m =
|
43
|
+
case @mode
|
44
|
+
when :research
|
45
|
+
m = /JSON:\n+```json\s*(\{.+\})\s*```\n\n/m.match(@template)
|
66
46
|
json = m[1].gsub(/(?!\\\\\\)\\\\"/) { '\\\"' }
|
67
47
|
res = JSON.parse(json)
|
68
48
|
res["messages"] = @messages
|
69
49
|
res
|
70
|
-
when
|
50
|
+
when :normal
|
71
51
|
@messages
|
72
52
|
end
|
73
53
|
end
|
@@ -76,8 +56,9 @@ class MonadicApp
|
|
76
56
|
params = @params.dup
|
77
57
|
|
78
58
|
@update_proc.call
|
79
|
-
|
80
|
-
|
59
|
+
|
60
|
+
case @mode
|
61
|
+
when :research
|
81
62
|
messages = +""
|
82
63
|
system = +""
|
83
64
|
@messages.each do |mes|
|
@@ -87,20 +68,28 @@ class MonadicApp
|
|
87
68
|
when "system"
|
88
69
|
system << "#{content}\n"
|
89
70
|
when "assistant", "gpt"
|
90
|
-
|
71
|
+
messages << "- #{mes["role"].strip}: #{content}\n"
|
91
72
|
else
|
92
|
-
messages << "- #{mes["role"].strip}: #{mes["content"]}"
|
73
|
+
messages << "- #{mes["role"].strip}: #{mes["content"]}\n"
|
93
74
|
end
|
94
75
|
end
|
95
76
|
template = @template.dup.sub("{{SYSTEM}}", system)
|
96
77
|
.sub("{{PROMPT}}", input)
|
97
78
|
.sub("{{MESSAGES}}", messages.strip)
|
98
79
|
|
99
|
-
|
80
|
+
File.open(TEMP_MD, "w") { |f| f.write template }
|
81
|
+
|
100
82
|
@messages << { "role" => "user", "content" => input }
|
101
|
-
|
83
|
+
|
84
|
+
case @method
|
85
|
+
when "completions"
|
86
|
+
params["prompt"] = template
|
87
|
+
when "chat/completions"
|
88
|
+
params["messages"] = [{ "role" => "system", "content" => template }]
|
89
|
+
end
|
90
|
+
|
91
|
+
when :normal
|
102
92
|
@messages << { "role" => "user", "content" => input }
|
103
|
-
@update_proc.call
|
104
93
|
params["messages"] = @messages
|
105
94
|
end
|
106
95
|
|
@@ -108,36 +97,31 @@ class MonadicApp
|
|
108
97
|
end
|
109
98
|
|
110
99
|
def update_template(res)
|
111
|
-
case @
|
112
|
-
when
|
100
|
+
case @mode
|
101
|
+
when :research
|
113
102
|
@metadata = res
|
114
|
-
@messages << { "role" => "assistant", "content" =>
|
115
|
-
json =
|
116
|
-
|
117
|
-
|
103
|
+
@messages << { "role" => "assistant", "content" => @metadata["response"] }
|
104
|
+
json = @metadata.to_json.strip
|
105
|
+
File.open(TEMP_JSON, "w") { |f| f.write json }
|
106
|
+
@template.sub!(/JSON:\n+```json.+```\n\n/m, "JSON:\n\n```json\n#{json}\n```\n\n")
|
107
|
+
when :normal
|
118
108
|
@messages << { "role" => "assistant", "content" => res }
|
119
109
|
end
|
120
110
|
end
|
121
111
|
|
122
112
|
##################################################
|
123
|
-
#
|
113
|
+
# function to bind data
|
124
114
|
##################################################
|
125
115
|
|
126
|
-
def
|
116
|
+
def bind(input, num_retry: 0)
|
127
117
|
print PROMPT_ASSISTANT.prefix, "\n"
|
128
|
-
print TTY::Cursor.save
|
129
|
-
|
130
|
-
wait
|
131
|
-
|
132
118
|
params = prepare_params(input)
|
133
|
-
|
134
|
-
print TTY::Cursor.save
|
119
|
+
research_mode = @mode == :research
|
135
120
|
|
136
121
|
escaping = +""
|
137
122
|
last_chunk = +""
|
138
|
-
|
139
|
-
|
140
|
-
res = @completion.run(params, num_retry: num_retry) do |chunk|
|
123
|
+
|
124
|
+
res = @completion.run(params, research_mode: research_mode, num_retry: num_retry) do |chunk|
|
141
125
|
if escaping
|
142
126
|
chunk = escaping + chunk
|
143
127
|
escaping = ""
|
@@ -147,147 +131,17 @@ class MonadicApp
|
|
147
131
|
escaping += chunk
|
148
132
|
next
|
149
133
|
else
|
150
|
-
chunk = chunk.gsub('\\n'
|
151
|
-
response << chunk
|
152
|
-
end
|
153
|
-
|
154
|
-
if count_lines_below > 1
|
155
|
-
print PASTEL.magenta(last_chunk)
|
156
|
-
elsif !spinning
|
157
|
-
print PASTEL.red SPINNER
|
158
|
-
spinning = true
|
134
|
+
chunk = chunk.gsub('\\n') { "\n" }
|
159
135
|
end
|
160
136
|
|
137
|
+
print last_chunk
|
161
138
|
last_chunk = chunk
|
162
139
|
end
|
163
140
|
|
164
|
-
print
|
165
|
-
print TTY::Cursor.clear_screen_down
|
166
|
-
|
167
|
-
text = response.gsub(/(?<![\\>\s])(?!\n[\n<])\n/m) { "{{NEWLINE}}\n" }
|
168
|
-
text = text.gsub(/```(.+?)```/m) do
|
169
|
-
m = Regexp.last_match
|
170
|
-
"```#{m[1].gsub("{{NEWLINE}}\n") { "\n" }}```"
|
171
|
-
end
|
172
|
-
text = text.gsub(/`(.+?)`/) do
|
173
|
-
m = Regexp.last_match
|
174
|
-
"`#{m[1].gsub("{{NEWLINE}}\n") { "\n" }}`"
|
175
|
-
end
|
176
|
-
|
177
|
-
text = text.gsub(/(?!\\\\)\\/) { "" }
|
178
|
-
print TTY::Markdown.parse(text).gsub("{{NEWLINE}}") { "\n" }.strip
|
141
|
+
print last_chunk
|
179
142
|
print "\n"
|
180
143
|
|
181
144
|
update_template(res)
|
182
145
|
set_html if @html
|
183
146
|
end
|
184
|
-
|
185
|
-
def bind_research_mode(input, num_retry: 0)
|
186
|
-
print PROMPT_ASSISTANT.prefix, "\n"
|
187
|
-
|
188
|
-
wait
|
189
|
-
|
190
|
-
params = prepare_params(input)
|
191
|
-
|
192
|
-
print TTY::Cursor.save
|
193
|
-
|
194
|
-
@threads << true
|
195
|
-
Thread.new do
|
196
|
-
response_all_shown = false
|
197
|
-
key_start = /"#{@prop_newdata}":\s*"/
|
198
|
-
key_finish = /\s+###\s*"/m
|
199
|
-
started = false
|
200
|
-
escaping = +""
|
201
|
-
last_chunk = +""
|
202
|
-
finished = false
|
203
|
-
response = +""
|
204
|
-
spinning = false
|
205
|
-
res = @completion.run(params, num_retry: num_retry, tmp_json_file: TEMP_JSON, tmp_md_file: TEMP_MD) do |chunk|
|
206
|
-
if finished && !response_all_shown
|
207
|
-
response_all_shown = true
|
208
|
-
@responses << response.sub(/\s+###\s*".*/m, "")
|
209
|
-
if spinning
|
210
|
-
TTY::Cursor.backword(" ▹▹▹▹▹ ".size)
|
211
|
-
TTY::Cursor.clear_char(" ▹▹▹▹▹ ".size)
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
unless finished
|
216
|
-
if escaping
|
217
|
-
chunk = escaping + chunk
|
218
|
-
escaping = ""
|
219
|
-
end
|
220
|
-
|
221
|
-
if /(?:\\\z)/ =~ chunk
|
222
|
-
escaping += chunk
|
223
|
-
next
|
224
|
-
else
|
225
|
-
chunk = chunk.gsub('\\n', "\n")
|
226
|
-
response << chunk
|
227
|
-
end
|
228
|
-
|
229
|
-
if started && !finished
|
230
|
-
if key_finish =~ response
|
231
|
-
finished = true
|
232
|
-
else
|
233
|
-
if count_lines_below > 1
|
234
|
-
print PASTEL.magenta(last_chunk)
|
235
|
-
elsif !spinning
|
236
|
-
print PASTEL.red SPINNER
|
237
|
-
spinning = true
|
238
|
-
end
|
239
|
-
last_chunk = chunk
|
240
|
-
end
|
241
|
-
elsif !started && !finished && key_start =~ response
|
242
|
-
started = true
|
243
|
-
response = +""
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
unless response_all_shown
|
249
|
-
if spinning
|
250
|
-
TTY::Cursor.backword(SPINNER.size)
|
251
|
-
TTY::Cursor.clear_char(SPINNER.size)
|
252
|
-
end
|
253
|
-
@responses << response.sub(/\s+###\s*".*/m, "")
|
254
|
-
end
|
255
|
-
|
256
|
-
update_template(res)
|
257
|
-
@threads.clear
|
258
|
-
rescue StandardError => e
|
259
|
-
@threads.clear
|
260
|
-
@responses << <<~ERROR
|
261
|
-
Error: something went wrong in a thread"
|
262
|
-
#{e.message}
|
263
|
-
#{e.backtrace}
|
264
|
-
ERROR
|
265
|
-
end
|
266
|
-
|
267
|
-
loop do
|
268
|
-
if @responses.empty?
|
269
|
-
sleep 1
|
270
|
-
else
|
271
|
-
print TTY::Cursor.restore
|
272
|
-
print TTY::Cursor.clear_screen_down
|
273
|
-
text = @responses.pop
|
274
|
-
|
275
|
-
text = text.gsub(/(?<![\\>\s])(?!\n[\n<])\n/m) { "{{NEWLINE}}\n" }
|
276
|
-
text = text.gsub(/```(.+?)```/m) do
|
277
|
-
m = Regexp.last_match
|
278
|
-
"```#{m[1].gsub("{{NEWLINE}}\n") { "\n" }}```"
|
279
|
-
end
|
280
|
-
text = text.gsub(/`(.+?)`/) do
|
281
|
-
m = Regexp.last_match
|
282
|
-
"`#{m[1].gsub("{{NEWLINE}}\n") { "\n" }}`"
|
283
|
-
end
|
284
|
-
|
285
|
-
text = text.gsub(/(?!\\\\)\\/) { "" }
|
286
|
-
print TTY::Markdown.parse(text).gsub("{{NEWLINE}}") { "\n" }.strip
|
287
|
-
print "\n"
|
288
|
-
break
|
289
|
-
end
|
290
|
-
end
|
291
|
-
set_html if @html
|
292
|
-
end
|
293
147
|
end
|
data/lib/monadic_chat/menu.rb
CHANGED
@@ -127,13 +127,13 @@ class MonadicApp
|
|
127
127
|
|
128
128
|
begin
|
129
129
|
File.open(filepath, "w") do |f|
|
130
|
-
case @
|
131
|
-
when
|
132
|
-
m =
|
130
|
+
case @mode
|
131
|
+
when :research
|
132
|
+
m = /JSON:\n+```json\s*(\{.+\})\s*```\n\n/m.match(@template)
|
133
133
|
data = JSON.parse(m[1])
|
134
134
|
data["messages"] = @messages
|
135
135
|
f.write JSON.pretty_generate(data)
|
136
|
-
when
|
136
|
+
when :normal
|
137
137
|
f.write JSON.pretty_generate({ "messages" => @messages })
|
138
138
|
end
|
139
139
|
|
@@ -167,15 +167,15 @@ class MonadicApp
|
|
167
167
|
filepath = File.expand_path(input.strip)
|
168
168
|
json = File.read(filepath)
|
169
169
|
data = JSON.parse(json)
|
170
|
-
case @
|
171
|
-
when
|
170
|
+
case @mode
|
171
|
+
when :research
|
172
172
|
self.class.name.downcase.split("::")[-1]
|
173
173
|
|
174
174
|
raise unless data["mode"] == self.class.name.downcase.split("::")[-1]
|
175
175
|
|
176
176
|
@messages = data.delete "messages"
|
177
|
-
@template = @template.sub(
|
178
|
-
when
|
177
|
+
@template = @template.sub(/JSON:\n+```json\s*\{.+\}\s*```\n\n/m, "JSON:\n\n```json\n#{JSON.pretty_generate(data).strip}\n```\n\n")
|
178
|
+
when :normal
|
179
179
|
raise unless data["messages"] && data["messages"][0]["role"]
|
180
180
|
|
181
181
|
@messages = data["messages"]
|
data/lib/monadic_chat/open_ai.rb
CHANGED
@@ -12,7 +12,11 @@ Oj.mimic_JSON
|
|
12
12
|
|
13
13
|
module OpenAI
|
14
14
|
def self.model_name(research_mode: false)
|
15
|
-
research_mode
|
15
|
+
if research_mode
|
16
|
+
"text-davinci-003"
|
17
|
+
else
|
18
|
+
"gpt-3.5-turbo"
|
19
|
+
end
|
16
20
|
end
|
17
21
|
|
18
22
|
def self.model_to_method(model)
|
@@ -85,15 +89,25 @@ module OpenAI
|
|
85
89
|
class Completion
|
86
90
|
attr_reader :access_token
|
87
91
|
|
88
|
-
def initialize(access_token)
|
92
|
+
def initialize(access_token, normal_mode_model = nil, research_mode_model = nil)
|
89
93
|
@access_token = access_token
|
94
|
+
@normal_mode_model = normal_mode_model || OpenAI.model_name(research_mode: false)
|
95
|
+
@research_mode_model = research_mode_model || OpenAI.model_name(research_mode: true)
|
96
|
+
end
|
97
|
+
|
98
|
+
def model_name(research_mode: false)
|
99
|
+
if research_mode
|
100
|
+
@research_mode_model
|
101
|
+
else
|
102
|
+
@normal_mode_model
|
103
|
+
end
|
90
104
|
end
|
91
105
|
|
92
106
|
def models
|
93
107
|
OpenAI.models(@access_token)
|
94
108
|
end
|
95
109
|
|
96
|
-
def run(params,
|
110
|
+
def run(params, research_mode: false, num_retry: 1, &block)
|
97
111
|
method = OpenAI.model_to_method(params["model"])
|
98
112
|
|
99
113
|
response = OpenAI.query(@access_token, "post", method, 60, params, &block)
|
@@ -103,12 +117,10 @@ module OpenAI
|
|
103
117
|
raise "finished because of length"
|
104
118
|
end
|
105
119
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
when "chat/completions"
|
111
|
-
response ["choices"][0]["text"]
|
120
|
+
if research_mode
|
121
|
+
get_json response["choices"][0]["text"]
|
122
|
+
else
|
123
|
+
response["choices"][0]["text"]
|
112
124
|
end
|
113
125
|
rescue StandardError => e
|
114
126
|
case num_retry
|
@@ -119,9 +131,9 @@ module OpenAI
|
|
119
131
|
end
|
120
132
|
end
|
121
133
|
|
122
|
-
def get_json(data
|
134
|
+
def get_json(data)
|
123
135
|
case data
|
124
|
-
when %r{<JSON>\n*(\{
|
136
|
+
when %r{<JSON>\n*(\{.+?\})\n*</JSON>}m
|
125
137
|
json = Regexp.last_match(1).gsub(/\r\n?/, "\n").gsub(/\r\n/) { "\n" }
|
126
138
|
res = JSON.parse(json)
|
127
139
|
when /(\{.+\})/m
|
@@ -130,7 +142,6 @@ module OpenAI
|
|
130
142
|
else
|
131
143
|
res = data
|
132
144
|
end
|
133
|
-
File.open(tmp_json_file, "w") { |f| f.write json } if tmp_json_file
|
134
145
|
res
|
135
146
|
end
|
136
147
|
|
@@ -143,9 +154,9 @@ module OpenAI
|
|
143
154
|
prompts.each do |prompt|
|
144
155
|
params["prompt"] = template.sub(replace_key, prompt)
|
145
156
|
res = run(params, num_retry: num_retry)
|
146
|
-
json = JSON.pretty_generate(res)
|
157
|
+
json = JSON.pretty_generate(get_json(res))
|
147
158
|
bar.advance(1)
|
148
|
-
template = template.sub(
|
159
|
+
template = template.sub(/JSON:\n+```json.+?```\n\n/m, "JSON:\n\n```json\n#{json}\n```\n\n")
|
149
160
|
end
|
150
161
|
bar.finish
|
151
162
|
JSON.parse(json)
|
@@ -20,11 +20,7 @@ class MonadicApp
|
|
20
20
|
case parameter
|
21
21
|
when "model"
|
22
22
|
value = change_model
|
23
|
-
@
|
24
|
-
case @method
|
25
|
-
when RESEARCH_MODE
|
26
|
-
@template = @template_initial.dup
|
27
|
-
end
|
23
|
+
@method = OpenAI.model_to_method(value)
|
28
24
|
when "max_tokens"
|
29
25
|
value = change_max_tokens
|
30
26
|
when "temperature"
|
@@ -79,13 +75,19 @@ class MonadicApp
|
|
79
75
|
model = PROMPT_SYSTEM.select("Select a model:", per_page: 10, cycle: false, show_help: :never, filter: true, default: 1) do |menu|
|
80
76
|
menu.choice "#{BULLET} Cancel", "cancel"
|
81
77
|
TTY::Cursor.save
|
82
|
-
|
78
|
+
SPINNER.auto_spin
|
83
79
|
models = @completion.models
|
84
|
-
|
85
|
-
TTY::Cursor.restore
|
80
|
+
SPINNER.stop
|
86
81
|
TTY::Cursor.restore
|
87
|
-
|
88
|
-
|
82
|
+
case @mode
|
83
|
+
when :research
|
84
|
+
models.filter { |m| ["completions", "chat/completions"].include? OpenAI.model_to_method(m["id"]) }.sort_by { |m| -m["created"] }.each do |m|
|
85
|
+
menu.choice "#{BULLET} #{m["id"]}", m["id"]
|
86
|
+
end
|
87
|
+
when :normal
|
88
|
+
models.filter { |m| OpenAI.model_to_method(m["id"]) == "chat/completions" }.sort_by { |m| -m["created"] }.each do |m|
|
89
|
+
menu.choice "#{BULLET} #{m["id"]}", m["id"]
|
90
|
+
end
|
89
91
|
end
|
90
92
|
end
|
91
93
|
if model == "cancel"
|
data/lib/monadic_chat/version.rb
CHANGED
data/lib/monadic_chat.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "tty-cursor"
|
4
4
|
require "tty-screen"
|
5
5
|
require "tty-markdown"
|
6
|
+
require "tty-spinner"
|
6
7
|
require "tty-prompt"
|
7
8
|
require "tty-box"
|
8
9
|
require "pastel"
|
@@ -25,8 +26,6 @@ module MonadicChat
|
|
25
26
|
MIN_LENGTH = 5
|
26
27
|
TIMEOUT_SEC = 120
|
27
28
|
TITLE_WIDTH = 72
|
28
|
-
NORMAL_MODE = "chat/completions"
|
29
|
-
RESEARCH_MODE = "completions"
|
30
29
|
|
31
30
|
APPS_DIR = File.absolute_path(File.join(__dir__, "..", "apps"))
|
32
31
|
APPS_DIR_LIST = Dir.entries(APPS_DIR)
|
@@ -123,15 +122,37 @@ module MonadicChat
|
|
123
122
|
end
|
124
123
|
|
125
124
|
def self.authenticate(overwrite: false)
|
126
|
-
check = lambda do |token|
|
127
|
-
print "Checking configuration
|
125
|
+
check = lambda do |token, normal_mode_model, research_mode_model|
|
126
|
+
print "Checking configuration\n"
|
127
|
+
SPINNER.auto_spin
|
128
128
|
begin
|
129
|
-
|
129
|
+
models = OpenAI.models(token)
|
130
|
+
raise if models.empty?
|
130
131
|
|
131
|
-
|
132
|
-
|
132
|
+
SPINNER.stop
|
133
|
+
|
134
|
+
print "Success\n"
|
135
|
+
|
136
|
+
if normal_mode_model && !models.map { |m| m["id"] }.index(normal_mode_model)
|
137
|
+
SPINNER.stop
|
138
|
+
print "Normal mode model set in config file not available.\n"
|
139
|
+
normal_mode_model = false
|
140
|
+
end
|
141
|
+
normal_mode_model ||= OpenAI.model_name(research_mode: false)
|
142
|
+
print "Normal mode model: #{normal_mode_model}\n"
|
143
|
+
|
144
|
+
if research_mode_model && !models.map { |m| m["id"] }.index(research_mode_model)
|
145
|
+
SPINNER.stop
|
146
|
+
print "Normal mode model set in config file not available.\n"
|
147
|
+
print "Fallback to the default model (#{OpenAI.model_name(research_mode: true)}).\n"
|
148
|
+
end
|
149
|
+
research_mode_model ||= OpenAI.model_name(research_mode: true)
|
150
|
+
print "Research mode model: #{research_mode_model}\n"
|
151
|
+
|
152
|
+
OpenAI::Completion.new(token, normal_mode_model, research_mode_model)
|
133
153
|
rescue StandardError
|
134
|
-
|
154
|
+
SPINNER.stop
|
155
|
+
print "Authentication: failure.\n"
|
135
156
|
false
|
136
157
|
end
|
137
158
|
end
|
@@ -142,7 +163,7 @@ module MonadicChat
|
|
142
163
|
access_token = PROMPT_SYSTEM.ask(" Input your OpenAI access token:")
|
143
164
|
return false if access_token.to_s == ""
|
144
165
|
|
145
|
-
completion = check.call(access_token)
|
166
|
+
completion = check.call(access_token, nil, nil)
|
146
167
|
|
147
168
|
if completion
|
148
169
|
File.open(CONFIG, "w") do |f|
|
@@ -153,12 +174,19 @@ module MonadicChat
|
|
153
174
|
end
|
154
175
|
elsif File.exist?(CONFIG)
|
155
176
|
json = File.read(CONFIG)
|
156
|
-
|
177
|
+
begin
|
178
|
+
config = JSON.parse(json)
|
179
|
+
rescue JSON::ParserError
|
180
|
+
puts "Error: config file does not contain a valid JSON object."
|
181
|
+
exit
|
182
|
+
end
|
157
183
|
access_token = config["access_token"]
|
158
|
-
|
184
|
+
normal_mode_model = config["normal_mode_model"]
|
185
|
+
research_mode_model = config["research_mode_model"]
|
186
|
+
completion = check.call(access_token, normal_mode_model, research_mode_model)
|
159
187
|
else
|
160
188
|
access_token ||= PROMPT_SYSTEM.ask(" Input your OpenAI access token:")
|
161
|
-
completion = check.call(access_token)
|
189
|
+
completion = check.call(access_token, nil, nil)
|
162
190
|
if completion
|
163
191
|
File.open(CONFIG, "w") do |f|
|
164
192
|
config = { "access_token" => access_token }
|
@@ -194,6 +222,8 @@ module MonadicChat
|
|
194
222
|
PROMPT_USER = TTY::PromptX.new(active_color: :blue, prefix: prompt_user)
|
195
223
|
PROMPT_SYSTEM = TTY::PromptX.new(active_color: :blue, prefix: "#{prompt_system} ")
|
196
224
|
PROMPT_ASSISTANT = TTY::PromptX.new(active_color: :red, prefix: "#{prompt_assistant} ")
|
197
|
-
|
225
|
+
|
226
|
+
SPINNER = TTY::Spinner.new(format: :arrow_pulse, clear: true)
|
227
|
+
|
198
228
|
BULLET = "\e[33m●\e[0m"
|
199
229
|
end
|
data/monadic_chat.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: monadic-chat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yohasebe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -234,6 +234,20 @@ dependencies:
|
|
234
234
|
- - ">="
|
235
235
|
- !ruby/object:Gem::Version
|
236
236
|
version: '0'
|
237
|
+
- !ruby/object:Gem::Dependency
|
238
|
+
name: tty-spinner
|
239
|
+
requirement: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - ">="
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: '0'
|
244
|
+
type: :runtime
|
245
|
+
prerelease: false
|
246
|
+
version_requirements: !ruby/object:Gem::Requirement
|
247
|
+
requirements:
|
248
|
+
- - ">="
|
249
|
+
- !ruby/object:Gem::Version
|
250
|
+
version: '0'
|
237
251
|
description: 'Monadic Chat is a command-line client application program that uses
|
238
252
|
OpenAI''s Text Completion API and Chat API to enable chat-style conversations with
|
239
253
|
OpenAI''s artificial intelligence system in a ChatGPT-like style.
|
@@ -275,10 +289,8 @@ files:
|
|
275
289
|
- doc/img/code-example-time-html.png
|
276
290
|
- doc/img/code-example-time.png
|
277
291
|
- doc/img/example-translation.png
|
278
|
-
- doc/img/extra-template-json.png
|
279
292
|
- doc/img/how-research-mode-works.svg
|
280
293
|
- doc/img/input-acess-token.png
|
281
|
-
- doc/img/langacker-2001.svg
|
282
294
|
- doc/img/linguistic-html.png
|
283
295
|
- doc/img/monadic-chat.svg
|
284
296
|
- doc/img/readme-example-beatles-html.png
|
Binary file
|