ollama_chat 0.0.9 → 0.0.10
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/CHANGES.md +10 -0
- data/VERSION +1 -1
- data/bin/ollama_chat_send +2 -1
- data/lib/ollama_chat/chat.rb +237 -205
- data/lib/ollama_chat/server_socket.rb +2 -2
- data/lib/ollama_chat/version.rb +1 -1
- data/ollama_chat.gemspec +2 -2
- data/spec/ollama_chat/chat_spec.rb +186 -11
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7e85d325cc40d5cb5dbe01e9971c22da13b4afada35fbb17f130d9430b1f508
|
4
|
+
data.tar.gz: 275b88f332344e7d1664e58c60540243d6cf512b2ba78547fbe784cfd02a6acc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b3a8bbf023960e38a33f4f2c4fc1e75e7e7be4c11213144975b1ad370efd9a71e4d61c76a7de3f3340d80eaa77d53882184f1f7635d7d5f678753e2e2c0dc6f
|
7
|
+
data.tar.gz: 69f57301a1308f75d55842630f7902172c8b67cf37d0ea2aad7564423be0567beb9e70bdff81b6e485cf2b3700040d6039e7c170d6a6bc4ebf65e1bf32bb32f9
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# Changes
|
2
2
|
|
3
|
+
## 2025-05-28 v0.0.10
|
4
|
+
|
5
|
+
* Simplify and improve command handling logic.
|
6
|
+
* Update chat input handling to use a single `handle_input` method for all commands.
|
7
|
+
* Add tests for various chat commands, including input handling, document
|
8
|
+
policy selection, summarization, and more.
|
9
|
+
* Improve test coverage for `DocumentCache`, `Information`, and other modules.
|
10
|
+
* Improved handling of commands, e.g. **don't** when sending via `ollama_chat_send` by default.
|
11
|
+
* Added support for sending content to server socket with specific type.
|
12
|
+
|
3
13
|
## 2025-05-26 v0.0.9
|
4
14
|
|
5
15
|
* Improved tag parsing in OllamaChat:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.10
|
data/bin/ollama_chat_send
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
require 'ollama_chat'
|
4
4
|
|
5
5
|
begin
|
6
|
-
|
6
|
+
type = (ARGV.shift || 'socket_input').to_sym
|
7
|
+
OllamaChat::ServerSocket.send_to_server_socket(STDIN.read, type:)
|
7
8
|
rescue => e
|
8
9
|
warn "Caught #{e.class}: #{e}"
|
9
10
|
exit 1
|
data/lib/ollama_chat/chat.rb
CHANGED
@@ -106,229 +106,261 @@ class OllamaChat::Chat
|
|
106
106
|
|
107
107
|
private
|
108
108
|
|
109
|
+
def handle_input(content)
|
110
|
+
case content
|
111
|
+
when %r(^/copy$)
|
112
|
+
copy_to_clipboard
|
113
|
+
:next
|
114
|
+
when %r(^/paste$)
|
115
|
+
paste_from_input
|
116
|
+
when %r(^/markdown$)
|
117
|
+
markdown.toggle
|
118
|
+
:next
|
119
|
+
when %r(^/stream$)
|
120
|
+
stream.toggle
|
121
|
+
:next
|
122
|
+
when %r(^/location$)
|
123
|
+
location.toggle
|
124
|
+
:next
|
125
|
+
when %r(^/voice(?:\s+(change))?$)
|
126
|
+
if $1 == 'change'
|
127
|
+
change_voice
|
128
|
+
else
|
129
|
+
voice.toggle
|
130
|
+
end
|
131
|
+
:next
|
132
|
+
when %r(^/list(?:\s+(\d*))?$)
|
133
|
+
last = 2 * $1.to_i if $1
|
134
|
+
messages.list_conversation(last)
|
135
|
+
:next
|
136
|
+
when %r(^/clear(?:\s+(messages|links|history|all))?$)
|
137
|
+
clean($1)
|
138
|
+
:next
|
139
|
+
when %r(^/clobber$)
|
140
|
+
clean('all')
|
141
|
+
:next
|
142
|
+
when %r(^/drop(?:\s+(\d*))?$)
|
143
|
+
messages.drop($1)
|
144
|
+
messages.list_conversation(2)
|
145
|
+
:next
|
146
|
+
when %r(^/model$)
|
147
|
+
@model = choose_model('', @model)
|
148
|
+
:next
|
149
|
+
when %r(^/system$)
|
150
|
+
change_system_prompt(@system)
|
151
|
+
info
|
152
|
+
:next
|
153
|
+
when %r(^/regenerate$)
|
154
|
+
if content = messages.second_last&.content
|
155
|
+
content.gsub!(/\nConsider these chunks for your answer.*\z/, '')
|
156
|
+
messages.drop(2)
|
157
|
+
else
|
158
|
+
STDOUT.puts "Not enough messages in this conversation."
|
159
|
+
return :redo
|
160
|
+
end
|
161
|
+
@parse_content = false
|
162
|
+
content
|
163
|
+
when %r(^/collection(?:\s+(clear|change))?$)
|
164
|
+
case $1 || 'change'
|
165
|
+
when 'clear'
|
166
|
+
loop do
|
167
|
+
tags = @documents.tags.add('[EXIT]').add('[ALL]')
|
168
|
+
tag = OllamaChat::Utils::Chooser.choose(tags, prompt: 'Clear? %s')
|
169
|
+
case tag
|
170
|
+
when nil, '[EXIT]'
|
171
|
+
STDOUT.puts "Exiting chooser."
|
172
|
+
break
|
173
|
+
when '[ALL]'
|
174
|
+
if ask?(prompt: 'Are you sure? (y/n) ') =~ /\Ay/i
|
175
|
+
@documents.clear
|
176
|
+
STDOUT.puts "Cleared collection #{bold{@documents.collection}}."
|
177
|
+
break
|
178
|
+
else
|
179
|
+
STDOUT.puts 'Cancelled.'
|
180
|
+
sleep 3
|
181
|
+
end
|
182
|
+
when /./
|
183
|
+
@documents.clear(tags: [ tag ])
|
184
|
+
STDOUT.puts "Cleared tag #{tag} from collection #{bold{@documents.collection}}."
|
185
|
+
sleep 3
|
186
|
+
end
|
187
|
+
end
|
188
|
+
when 'change'
|
189
|
+
choose_collection(@documents.collection)
|
190
|
+
end
|
191
|
+
:next
|
192
|
+
when %r(^/info$)
|
193
|
+
info
|
194
|
+
:next
|
195
|
+
when %r(^/document_policy$)
|
196
|
+
choose_document_policy
|
197
|
+
:next
|
198
|
+
when %r(^/import\s+(.+))
|
199
|
+
@parse_content = false
|
200
|
+
import($1) or :next
|
201
|
+
when %r(^/summarize\s+(?:(\d+)\s+)?(.+))
|
202
|
+
@parse_content = false
|
203
|
+
summarize($2, words: $1) or :next
|
204
|
+
when %r(^/embedding$)
|
205
|
+
embedding_paused.toggle(show: false)
|
206
|
+
embedding.show
|
207
|
+
:next
|
208
|
+
when %r(^/embed\s+(.+))
|
209
|
+
@parse_content = false
|
210
|
+
embed($1) or :next
|
211
|
+
when %r(^/web\s+(?:(\d+)\s+)?(.+))
|
212
|
+
@parse_content = false
|
213
|
+
web($1, $2)
|
214
|
+
when %r(^/save\s+(.+)$)
|
215
|
+
messages.save_conversation($1)
|
216
|
+
STDOUT.puts "Saved conversation to #$1."
|
217
|
+
:next
|
218
|
+
when %r(^/links(?:\s+(clear))?$)
|
219
|
+
manage_links($1)
|
220
|
+
:next
|
221
|
+
when %r(^/load\s+(.+)$)
|
222
|
+
messages.load_conversation($1)
|
223
|
+
if messages.size > 1
|
224
|
+
messages.list_conversation(2)
|
225
|
+
end
|
226
|
+
STDOUT.puts "Loaded conversation from #$1."
|
227
|
+
:next
|
228
|
+
when %r(^/config$)
|
229
|
+
display_config
|
230
|
+
:next
|
231
|
+
when %r(^/quit$), nil
|
232
|
+
STDOUT.puts "Goodbye."
|
233
|
+
:return
|
234
|
+
when %r(^/)
|
235
|
+
display_chat_help
|
236
|
+
:next
|
237
|
+
when /\A\s*\z/
|
238
|
+
STDOUT.puts "Type /quit to quit."
|
239
|
+
:next
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def web(count, query)
|
244
|
+
urls = search_web(query, count.to_i) or return :next
|
245
|
+
urls.each do |url|
|
246
|
+
fetch_source(url) { |url_io| embed_source(url_io, url) }
|
247
|
+
end
|
248
|
+
urls_summarized = urls.map { summarize(_1) }
|
249
|
+
query = $2.inspect
|
250
|
+
results = urls.zip(urls_summarized).
|
251
|
+
map { |u, s| "%s as \n:%s" % [ u, s ] } * "\n\n"
|
252
|
+
config.prompts.web % { query:, results: }
|
253
|
+
end
|
254
|
+
|
255
|
+
def manage_links(command)
|
256
|
+
case command
|
257
|
+
when 'clear'
|
258
|
+
loop do
|
259
|
+
links_options = links.dup.add('[EXIT]').add('[ALL]')
|
260
|
+
link = OllamaChat::Utils::Chooser.choose(links_options, prompt: 'Clear? %s')
|
261
|
+
case link
|
262
|
+
when nil, '[EXIT]'
|
263
|
+
STDOUT.puts "Exiting chooser."
|
264
|
+
break
|
265
|
+
when '[ALL]'
|
266
|
+
if ask?(prompt: 'Are you sure? (y/n) ') =~ /\Ay/i
|
267
|
+
links.clear
|
268
|
+
STDOUT.puts "Cleared all links in list."
|
269
|
+
break
|
270
|
+
else
|
271
|
+
STDOUT.puts 'Cancelled.'
|
272
|
+
sleep 3
|
273
|
+
end
|
274
|
+
when /./
|
275
|
+
links.delete(link)
|
276
|
+
STDOUT.puts "Cleared link from links in list."
|
277
|
+
sleep 3
|
278
|
+
end
|
279
|
+
end
|
280
|
+
when nil
|
281
|
+
if links.empty?
|
282
|
+
STDOUT.puts "List is empty."
|
283
|
+
else
|
284
|
+
Math.log10(links.size).ceil
|
285
|
+
format = "% #{}s. %s"
|
286
|
+
connect = -> link { hyperlink(link) { link } }
|
287
|
+
STDOUT.puts links.each_with_index.map { |x, i| format % [ i + 1, connect.(x) ] }
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def clean(what)
|
293
|
+
what = 'messages' if what.nil?
|
294
|
+
case what
|
295
|
+
when 'messages'
|
296
|
+
messages.clear
|
297
|
+
STDOUT.puts "Cleared messages."
|
298
|
+
when 'links'
|
299
|
+
links.clear
|
300
|
+
STDOUT.puts "Cleared links."
|
301
|
+
when 'history'
|
302
|
+
clear_history
|
303
|
+
STDOUT.puts "Cleared history."
|
304
|
+
when 'all'
|
305
|
+
if ask?(prompt: 'Are you sure to clear messages and collection? (y/n) ') =~ /\Ay/i
|
306
|
+
messages.clear
|
307
|
+
@documents.clear
|
308
|
+
links.clear
|
309
|
+
clear_history
|
310
|
+
STDOUT.puts "Cleared messages and collection #{bold{@documents.collection}}."
|
311
|
+
else
|
312
|
+
STDOUT.puts 'Cancelled.'
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def display_config
|
318
|
+
default_pager = ENV['PAGER'].full?
|
319
|
+
if fallback_pager = `which less`.chomp.full? || `which more`.chomp.full?
|
320
|
+
fallback_pager << ' -r'
|
321
|
+
end
|
322
|
+
my_pager = default_pager || fallback_pager
|
323
|
+
rendered = config.to_s
|
324
|
+
Kramdown::ANSI::Pager.pager(
|
325
|
+
lines: rendered.count(?\n),
|
326
|
+
command: my_pager
|
327
|
+
) do |output|
|
328
|
+
output.puts rendered
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
109
332
|
def interact_with_user
|
110
333
|
loop do
|
111
|
-
parse_content = true
|
112
|
-
|
334
|
+
@parse_content = true
|
335
|
+
type = :terminal_input
|
336
|
+
input_prompt = bold { color(172) { message_type(@images) + " user" } } + bold { "> " }
|
113
337
|
|
114
338
|
begin
|
115
339
|
content = Reline.readline(input_prompt, true)&.chomp
|
116
340
|
rescue Interrupt
|
117
341
|
if message = server_socket_message
|
118
342
|
self.server_socket_message = nil
|
343
|
+
type = message.fetch('type', 'socket_input').to_sym
|
119
344
|
content = message['content']
|
120
345
|
else
|
121
346
|
raise
|
122
347
|
end
|
123
348
|
end
|
124
349
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
content = paste_from_input
|
131
|
-
when %r(^/markdown$)
|
132
|
-
markdown.toggle
|
133
|
-
next
|
134
|
-
when %r(^/stream$)
|
135
|
-
stream.toggle
|
136
|
-
next
|
137
|
-
when %r(^/location$)
|
138
|
-
location.toggle
|
139
|
-
next
|
140
|
-
when %r(^/voice(?:\s+(change))?$)
|
141
|
-
if $1 == 'change'
|
142
|
-
change_voice
|
143
|
-
else
|
144
|
-
voice.toggle
|
145
|
-
end
|
146
|
-
next
|
147
|
-
when %r(^/list(?:\s+(\d*))?$)
|
148
|
-
last = 2 * $1.to_i if $1
|
149
|
-
messages.list_conversation(last)
|
150
|
-
next
|
151
|
-
when %r(^/clear(?:\s+(messages|links|history))?$)
|
152
|
-
what = $1.nil? ? 'messages' : $1
|
153
|
-
case what
|
154
|
-
when 'messages'
|
155
|
-
messages.clear
|
156
|
-
STDOUT.puts "Cleared messages."
|
157
|
-
when 'links'
|
158
|
-
links.clear
|
159
|
-
STDOUT.puts "Cleared links."
|
160
|
-
when 'history'
|
161
|
-
clear_history
|
162
|
-
STDOUT.puts "Cleared history."
|
163
|
-
end
|
164
|
-
next
|
165
|
-
when %r(^/clobber$)
|
166
|
-
if ask?(prompt: 'Are you sure to clear messages and collection? (y/n) ') =~ /\Ay/i
|
167
|
-
messages.clear
|
168
|
-
@documents.clear
|
169
|
-
links.clear
|
170
|
-
clear_history
|
171
|
-
STDOUT.puts "Cleared messages and collection #{bold{@documents.collection}}."
|
172
|
-
else
|
173
|
-
STDOUT.puts 'Cancelled.'
|
174
|
-
end
|
175
|
-
next
|
176
|
-
when %r(^/drop(?:\s+(\d*))?$)
|
177
|
-
messages.drop($1)
|
178
|
-
messages.list_conversation(2)
|
179
|
-
next
|
180
|
-
when %r(^/model$)
|
181
|
-
@model = choose_model('', @model)
|
182
|
-
next
|
183
|
-
when %r(^/system$)
|
184
|
-
change_system_prompt(@system)
|
185
|
-
info
|
186
|
-
next
|
187
|
-
when %r(^/regenerate$)
|
188
|
-
if content = messages.second_last&.content
|
189
|
-
content.gsub!(/\nConsider these chunks for your answer.*\z/, '')
|
190
|
-
messages.drop(2)
|
191
|
-
else
|
192
|
-
STDOUT.puts "Not enough messages in this conversation."
|
350
|
+
unless type == :socket_input
|
351
|
+
case next_action = handle_input(content)
|
352
|
+
when :next
|
353
|
+
next
|
354
|
+
when :redo
|
193
355
|
redo
|
356
|
+
when :return
|
357
|
+
return
|
358
|
+
when String
|
359
|
+
content = next_action
|
194
360
|
end
|
195
|
-
parse_content = false
|
196
|
-
content
|
197
|
-
when %r(^/collection(?:\s+(clear|change))?$)
|
198
|
-
case $1 || 'change'
|
199
|
-
when 'clear'
|
200
|
-
loop do
|
201
|
-
tags = @documents.tags.add('[EXIT]').add('[ALL]')
|
202
|
-
tag = OllamaChat::Utils::Chooser.choose(tags, prompt: 'Clear? %s')
|
203
|
-
case tag
|
204
|
-
when nil, '[EXIT]'
|
205
|
-
STDOUT.puts "Exiting chooser."
|
206
|
-
break
|
207
|
-
when '[ALL]'
|
208
|
-
if ask?(prompt: 'Are you sure? (y/n) ') =~ /\Ay/i
|
209
|
-
@documents.clear
|
210
|
-
STDOUT.puts "Cleared collection #{bold{@documents.collection}}."
|
211
|
-
break
|
212
|
-
else
|
213
|
-
STDOUT.puts 'Cancelled.'
|
214
|
-
sleep 3
|
215
|
-
end
|
216
|
-
when /./
|
217
|
-
@documents.clear(tags: [ tag ])
|
218
|
-
STDOUT.puts "Cleared tag #{tag} from collection #{bold{@documents.collection}}."
|
219
|
-
sleep 3
|
220
|
-
end
|
221
|
-
end
|
222
|
-
when 'change'
|
223
|
-
choose_collection(@documents.collection)
|
224
|
-
end
|
225
|
-
next
|
226
|
-
when %r(^/info$)
|
227
|
-
info
|
228
|
-
next
|
229
|
-
when %r(^/document_policy$)
|
230
|
-
choose_document_policy
|
231
|
-
next
|
232
|
-
when %r(^/import\s+(.+))
|
233
|
-
parse_content = false
|
234
|
-
content = import($1) or next
|
235
|
-
when %r(^/summarize\s+(?:(\d+)\s+)?(.+))
|
236
|
-
parse_content = false
|
237
|
-
content = summarize($2, words: $1) or next
|
238
|
-
when %r(^/embedding$)
|
239
|
-
embedding_paused.toggle(show: false)
|
240
|
-
embedding.show
|
241
|
-
next
|
242
|
-
when %r(^/embed\s+(.+))
|
243
|
-
parse_content = false
|
244
|
-
content = embed($1) or next
|
245
|
-
when %r(^/web\s+(?:(\d+)\s+)?(.+))
|
246
|
-
parse_content = false
|
247
|
-
urls = search_web($2, $1.to_i) or next
|
248
|
-
urls.each do |url|
|
249
|
-
fetch_source(url) { |url_io| embed_source(url_io, url) }
|
250
|
-
end
|
251
|
-
urls_summarized = urls.map { summarize(_1) }
|
252
|
-
query = $2.inspect
|
253
|
-
results = urls.zip(urls_summarized).
|
254
|
-
map { |u, s| "%s as \n:%s" % [ u, s ] } * "\n\n"
|
255
|
-
content = config.prompts.web % { query:, results: }
|
256
|
-
when %r(^/save\s+(.+)$)
|
257
|
-
messages.save_conversation($1)
|
258
|
-
STDOUT.puts "Saved conversation to #$1."
|
259
|
-
next
|
260
|
-
when %r(^/links(?:\s+(clear))?$)
|
261
|
-
case $1
|
262
|
-
when 'clear'
|
263
|
-
loop do
|
264
|
-
links_options = links.dup.add('[EXIT]').add('[ALL]')
|
265
|
-
link = OllamaChat::Utils::Chooser.choose(links_options, prompt: 'Clear? %s')
|
266
|
-
case link
|
267
|
-
when nil, '[EXIT]'
|
268
|
-
STDOUT.puts "Exiting chooser."
|
269
|
-
break
|
270
|
-
when '[ALL]'
|
271
|
-
if ask?(prompt: 'Are you sure? (y/n) ') =~ /\Ay/i
|
272
|
-
links.clear
|
273
|
-
STDOUT.puts "Cleared all links in list."
|
274
|
-
break
|
275
|
-
else
|
276
|
-
STDOUT.puts 'Cancelled.'
|
277
|
-
sleep 3
|
278
|
-
end
|
279
|
-
when /./
|
280
|
-
links.delete(link)
|
281
|
-
STDOUT.puts "Cleared link from links in list."
|
282
|
-
sleep 3
|
283
|
-
end
|
284
|
-
end
|
285
|
-
when nil
|
286
|
-
if links.empty?
|
287
|
-
STDOUT.puts "List is empty."
|
288
|
-
else
|
289
|
-
Math.log10(links.size).ceil
|
290
|
-
format = "% #{}s. %s"
|
291
|
-
connect = -> link { hyperlink(link) { link } }
|
292
|
-
STDOUT.puts links.each_with_index.map { |x, i| format % [ i + 1, connect.(x) ] }
|
293
|
-
end
|
294
|
-
end
|
295
|
-
next
|
296
|
-
when %r(^/load\s+(.+)$)
|
297
|
-
messages.load_conversation($1)
|
298
|
-
if messages.size > 1
|
299
|
-
messages.list_conversation(2)
|
300
|
-
end
|
301
|
-
STDOUT.puts "Loaded conversation from #$1."
|
302
|
-
next
|
303
|
-
when %r(^/config$)
|
304
|
-
default_pager = ENV['PAGER'].full?
|
305
|
-
if fallback_pager = `which less`.chomp.full? || `which more`.chomp.full?
|
306
|
-
fallback_pager << ' -r'
|
307
|
-
end
|
308
|
-
my_pager = default_pager || fallback_pager
|
309
|
-
rendered = config.to_s
|
310
|
-
Kramdown::ANSI::Pager.pager(
|
311
|
-
lines: rendered.count(?\n),
|
312
|
-
command: my_pager
|
313
|
-
) do |output|
|
314
|
-
output.puts rendered
|
315
|
-
end
|
316
|
-
next
|
317
|
-
when %r(^/quit$)
|
318
|
-
STDOUT.puts "Goodbye."
|
319
|
-
return
|
320
|
-
when %r(^/)
|
321
|
-
display_chat_help
|
322
|
-
next
|
323
|
-
when ''
|
324
|
-
STDOUT.puts "Type /quit to quit."
|
325
|
-
next
|
326
|
-
when nil
|
327
|
-
STDOUT.puts "Goodbye."
|
328
|
-
return
|
329
361
|
end
|
330
362
|
|
331
|
-
content, tags = if parse_content
|
363
|
+
content, tags = if @parse_content
|
332
364
|
parse_content(content, @images)
|
333
365
|
else
|
334
366
|
[ content, Documentrix::Utils::Tags.new(valid_tag: /\A#*([\w\]\[]+)/) ]
|
@@ -8,9 +8,9 @@ module OllamaChat::ServerSocket
|
|
8
8
|
File.join(runtime_dir, 'ollama_chat.sock')
|
9
9
|
end
|
10
10
|
|
11
|
-
def send_to_server_socket(content)
|
11
|
+
def send_to_server_socket(content, type: :socket_input)
|
12
12
|
FileUtils.mkdir_p runtime_dir
|
13
|
-
message = { content: }
|
13
|
+
message = { content:, type: }
|
14
14
|
socket = UNIXSocket.new(server_socket_path)
|
15
15
|
socket.puts JSON(message)
|
16
16
|
socket.close
|
data/lib/ollama_chat/version.rb
CHANGED
data/ollama_chat.gemspec
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# stub: ollama_chat 0.0.
|
2
|
+
# stub: ollama_chat 0.0.10 ruby lib
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "ollama_chat".freeze
|
6
|
-
s.version = "0.0.
|
6
|
+
s.version = "0.0.10".freeze
|
7
7
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
9
9
|
s.require_paths = ["lib".freeze]
|
@@ -6,43 +6,211 @@ RSpec.describe OllamaChat::Chat do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
let :chat do
|
9
|
-
OllamaChat::Chat.new
|
9
|
+
OllamaChat::Chat.new(argv: argv).expose
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
describe 'instantiation' do
|
13
|
+
connect_to_ollama_server(instantiate: false)
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
it 'can be instantiated' do
|
16
|
+
expect(chat).to be_a described_class
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'handle_input' do
|
21
|
+
connect_to_ollama_server
|
22
|
+
|
23
|
+
it 'returns :next when input is "/copy"' do
|
24
|
+
expect(chat).to receive(:copy_to_clipboard)
|
25
|
+
expect(chat.handle_input("/copy")).to eq :next
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns :next when input is "/paste"' do
|
29
|
+
expect(chat).to receive(:paste_from_input).and_return "pasted this"
|
30
|
+
expect(chat.handle_input("/paste")).to eq "pasted this"
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns :next when input is "/markdown"' do
|
34
|
+
expect(chat.markdown).to receive(:toggle)
|
35
|
+
expect(chat.handle_input("/markdown")).to eq :next
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns :next when input is "/stream"' do
|
39
|
+
expect(chat.stream).to receive(:toggle)
|
40
|
+
expect(chat.handle_input("/stream")).to eq :next
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'returns :next when input is "/location"' do
|
44
|
+
expect(chat.location).to receive(:toggle)
|
45
|
+
expect(chat.handle_input("/location")).to eq :next
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns :next when input is "/voice(?:\s+(change))? "' do
|
49
|
+
expect(chat.voice).to receive(:toggle)
|
50
|
+
expect(chat.handle_input("/voice")).to eq :next
|
51
|
+
expect(chat).to receive(:change_voice)
|
52
|
+
expect(chat.handle_input("/voice change")).to eq :next
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'returns :next when input is "/list(?:\s+(\d*))? "' do
|
56
|
+
expect(chat.messages).to receive(:list_conversation).with(4)
|
57
|
+
expect(chat.handle_input("/list 2")).to eq :next
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'returns :next when input is "/clear(messages|links|history|all)"' do
|
61
|
+
expect(chat).to receive(:clean).with('messages')
|
62
|
+
expect(chat.handle_input("/clear messages")).to eq :next
|
63
|
+
expect(chat).to receive(:clean).with('links')
|
64
|
+
expect(chat.handle_input("/clear links")).to eq :next
|
65
|
+
expect(chat).to receive(:clean).with('history')
|
66
|
+
expect(chat.handle_input("/clear history")).to eq :next
|
67
|
+
expect(chat).to receive(:clean).with('all')
|
68
|
+
expect(chat.handle_input("/clear all")).to eq :next
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'returns :next when input is "/clobber"' do
|
72
|
+
expect(chat).to receive(:clean).with('all')
|
73
|
+
expect(chat.handle_input("/clobber")).to eq :next
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'returns :next when input is "/drop(?:\s+(\d*))?"' do
|
77
|
+
expect(chat.messages).to receive(:drop).with(?2)
|
78
|
+
expect(chat.messages).to receive(:list_conversation).with(2)
|
79
|
+
expect(chat.handle_input("/drop 2")).to eq :next
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns :next when input is "/model"' do
|
83
|
+
expect(chat).to receive(:choose_model).with('', 'llama3.1')
|
84
|
+
expect(chat.handle_input("/model")).to eq :next
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'returns :next when input is "/system"' do
|
88
|
+
expect(chat).to receive(:change_system_prompt).with(nil)
|
89
|
+
expect(chat).to receive(:info)
|
90
|
+
expect(chat.handle_input("/system")).to eq :next
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'returns :next when input is "/regenerate"' do
|
94
|
+
expect(STDOUT).to receive(:puts).with(/Not enough messages/)
|
95
|
+
expect(chat.handle_input("/regenerate")).to eq :redo
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'returns :next when input is "/collection(clear|change)"' do
|
99
|
+
expect(OllamaChat::Utils::Chooser).to receive(:choose)
|
100
|
+
expect(STDOUT).to receive(:puts).with(/Exiting/)
|
101
|
+
expect(chat.handle_input("/collection clear")).to eq :next
|
102
|
+
expect(OllamaChat::Utils::Chooser).to receive(:choose)
|
103
|
+
expect(chat).to receive(:info)
|
104
|
+
expect(STDOUT).to receive(:puts).with(/./)
|
105
|
+
expect(chat.handle_input("/collection change")).to eq :next
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'returns :next when input is "/info"' do
|
109
|
+
expect(chat).to receive(:info)
|
110
|
+
expect(chat.handle_input("/info")).to eq :next
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'returns :next when input is "/document_policy"' do
|
114
|
+
expect(chat).to receive(:choose_document_policy)
|
115
|
+
expect(chat.handle_input("/document_policy")).to eq :next
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'returns :next when input is "/import\s+(.+)"' do
|
119
|
+
expect(chat).to receive(:import).with('./some_file')
|
120
|
+
expect(chat.handle_input("/import ./some_file")).to eq :next
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'returns :next when input is "/summarize\s+(?:(\d+)\s+)?(.+)"' do
|
124
|
+
expect(chat).to receive(:summarize).with('./some_file', words: '23')
|
125
|
+
expect(chat.handle_input("/summarize 23 ./some_file")).to eq :next
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'returns :next when input is "/embedding"' do
|
129
|
+
expect(chat.embedding_paused).to receive(:toggle)
|
130
|
+
expect(chat.embedding).to receive(:show)
|
131
|
+
expect(chat.handle_input("/embedding")).to eq :next
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'returns :next when input is "/embed\s+(.+)"' do
|
135
|
+
expect(chat).to receive(:embed).with('./some_file')
|
136
|
+
expect(chat.handle_input("/embed ./some_file")).to eq :next
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'returns :next when input is "/web\s+(?:(\d+)\s+)?(.+)"' do
|
140
|
+
expect(chat).to receive(:web).with('23', 'query').and_return 'the response'
|
141
|
+
expect(chat.handle_input("/web 23 query")).to eq 'the response'
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'returns :next when input is "/save\s+(.+)$"' do
|
145
|
+
expect(chat.messages).to receive(:save_conversation).with('./some_file')
|
146
|
+
expect(chat.handle_input("/save ./some_file")).to eq :next
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'returns :next when input is "/links(?:\s+(clear))?$" ' do
|
150
|
+
expect(chat).to receive(:manage_links).with(nil)
|
151
|
+
expect(chat.handle_input("/links")).to eq :next
|
152
|
+
expect(chat).to receive(:manage_links).with('clear')
|
153
|
+
expect(chat.handle_input("/links clear")).to eq :next
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'returns :next when input is "/load\s+(.+)$"' do
|
157
|
+
expect(chat.messages).to receive(:load_conversation).with('./some_file')
|
158
|
+
expect(chat.handle_input("/load ./some_file")).to eq :next
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'returns :next when input is "/config"' do
|
162
|
+
expect(chat).to receive(:display_config)
|
163
|
+
expect(chat.handle_input("/config")).to eq :next
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'returns :next when input is "/quit"' do
|
167
|
+
expect(STDOUT).to receive(:puts).with(/Goodbye/)
|
168
|
+
expect(chat.handle_input("/quit")).to eq :return
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'returns :next when input is "/nixda"' do
|
172
|
+
expect(chat).to receive(:display_chat_help)
|
173
|
+
expect(chat.handle_input("/nixda")).to eq :next
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'returns :next when input is " "' do
|
177
|
+
expect(STDOUT).to receive(:puts).with(/to quit/)
|
178
|
+
expect(chat.handle_input(" ")).to eq :next
|
179
|
+
end
|
16
180
|
end
|
17
181
|
|
18
182
|
describe 'chat history' do
|
183
|
+
connect_to_ollama_server(instantiate: false)
|
184
|
+
|
19
185
|
it 'derives chat_history_filename' do
|
20
|
-
expect(chat.
|
186
|
+
expect(chat.chat_history_filename).to_not be_nil
|
21
187
|
end
|
22
188
|
|
23
189
|
it 'can save chat history' do
|
24
190
|
expect(File).to receive(:secure_write).with(
|
25
|
-
chat.
|
191
|
+
chat.chat_history_filename,
|
26
192
|
kind_of(String)
|
27
193
|
)
|
28
|
-
chat.
|
194
|
+
chat.save_history
|
29
195
|
end
|
30
196
|
|
31
197
|
it 'can initialize chat history' do
|
32
|
-
expect(File).to receive(:exist?).with(chat.
|
198
|
+
expect(File).to receive(:exist?).with(chat.chat_history_filename).
|
33
199
|
and_return true
|
34
|
-
expect(File).to receive(:open).with(chat.
|
35
|
-
chat.
|
200
|
+
expect(File).to receive(:open).with(chat.chat_history_filename, ?r)
|
201
|
+
chat.init_chat_history
|
36
202
|
end
|
37
203
|
|
38
204
|
it 'can clear history' do
|
39
205
|
chat
|
40
206
|
expect(Readline::HISTORY).to receive(:clear)
|
41
|
-
chat.
|
207
|
+
chat.clear_history
|
42
208
|
end
|
43
209
|
end
|
44
210
|
|
45
211
|
context 'loading conversations' do
|
212
|
+
connect_to_ollama_server(instantiate: false)
|
213
|
+
|
46
214
|
let :argv do
|
47
215
|
%w[ -C test -c ] << asset('conversation.json')
|
48
216
|
end
|
@@ -56,7 +224,10 @@ RSpec.describe OllamaChat::Chat do
|
|
56
224
|
end
|
57
225
|
|
58
226
|
describe OllamaChat::DocumentCache do
|
227
|
+
connect_to_ollama_server(instantiate: false)
|
228
|
+
|
59
229
|
context 'with MemoryCache' do
|
230
|
+
|
60
231
|
let :argv do
|
61
232
|
%w[ -M ]
|
62
233
|
end
|
@@ -77,6 +248,8 @@ RSpec.describe OllamaChat::Chat do
|
|
77
248
|
|
78
249
|
describe Documentrix::Documents do
|
79
250
|
context 'with documents' do
|
251
|
+
connect_to_ollama_server(instantiate: false)
|
252
|
+
|
80
253
|
let :argv do
|
81
254
|
%w[ -C test -D ] << asset('example.html')
|
82
255
|
end
|
@@ -90,6 +263,8 @@ RSpec.describe OllamaChat::Chat do
|
|
90
263
|
end
|
91
264
|
|
92
265
|
describe OllamaChat::Information do
|
266
|
+
connect_to_ollama_server(instantiate: false)
|
267
|
+
|
93
268
|
it 'has progname' do
|
94
269
|
expect(chat.progname).to eq 'ollama_chat'
|
95
270
|
end
|