ollama_chat 0.0.8 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36395404aa00934bda9f2229306a374143c30046bf7439188d369b9fe167f649
4
- data.tar.gz: 2dc4c482a17e31a86c347d557475af52eccbe6638f2487abc564c11b5ef379b7
3
+ metadata.gz: d7e85d325cc40d5cb5dbe01e9971c22da13b4afada35fbb17f130d9430b1f508
4
+ data.tar.gz: 275b88f332344e7d1664e58c60540243d6cf512b2ba78547fbe784cfd02a6acc
5
5
  SHA512:
6
- metadata.gz: ece58d0452a5695dc3fec1a1032a9e45a6e9400085e48f512c13b9473acd53596a55d69a7f9264689fdb0a3b3335b8223547efbc504c9a3052a85e5ef9a0cc89
7
- data.tar.gz: 11a052d21eed75504722adde01ff43c10213ba371887b7f376fdbe4b962cf3dd3bd550325341af16c2215101776ca14753cf353eec221f3f5e98fff386053f51
6
+ metadata.gz: 3b3a8bbf023960e38a33f4f2c4fc1e75e7e7be4c11213144975b1ad370efd9a71e4d61c76a7de3f3340d80eaa77d53882184f1f7635d7d5f678753e2e2c0dc6f
7
+ data.tar.gz: 69f57301a1308f75d55842630f7902172c8b67cf37d0ea2aad7564423be0567beb9e70bdff81b6e485cf2b3700040d6039e7c170d6a6bc4ebf65e1bf32bb32f9
data/CHANGES.md CHANGED
@@ -1,5 +1,26 @@
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
+
13
+ ## 2025-05-26 v0.0.9
14
+
15
+ * Improved tag parsing in OllamaChat:
16
+ * Added regex validation for valid tags to `Documentrix::Utils::Tags`.
17
+ * Modified `parse_content` method in `OllamaChat::Parsing` to handle valid tag formats.
18
+ * Updated `scan` methods in `content` processing to more correctly identify tags.
19
+ * Added option to explicitly open socket for receiving input from `ollama_chat_send`:
20
+ * Added new command-line option `-S` to enable server socket functionality.
21
+ * Updated `OllamaChat::Chat` class to include server socket initialization based on the new option.
22
+ * Modified usage message in `README.md` and `information.rb` files.
23
+
3
24
  ## 2025-05-23 v0.0.8
4
25
 
5
26
  * Introduce `fix_config` method to rescue `ComplexConfig` exceptions and prompt
@@ -13,7 +34,7 @@
13
34
 
14
35
  ## 2025-05-22 v0.0.7
15
36
 
16
- * Added `ollama_chat_send` executable in `/bin`, required 'ollama_chat' gem,
37
+ * Added `ollama_chat_send` executable in `/bin`, required `ollama_chat` gem,
17
38
  sent user input to Ollama server via
18
39
  `OllamaChat::ServerSocket.send_to_server_socket` method and handled
19
40
  exceptions and exit with non-zero status code if an error occurs.
data/README.md CHANGED
@@ -31,6 +31,7 @@ Usage: ollama_chat [OPTIONS]
31
31
  -D DOCUMENT load document and add to embeddings collection (multiple)
32
32
  -M use (empty) MemoryCache for this chat session
33
33
  -E disable embeddings for this chat session
34
+ -S open a socket to receive input from ollama_chat_send
34
35
  -V display the current version number and quit
35
36
  -h this help
36
37
  ```
data/Rakefile CHANGED
@@ -31,7 +31,7 @@ GemHadar do
31
31
 
32
32
  dependency 'excon', '~> 1.0'
33
33
  dependency 'ollama-ruby', '~> 1.0'
34
- dependency 'documentrix', '~> 0.0'
34
+ dependency 'documentrix', '~> 0.0', '>= 0.0.2'
35
35
  dependency 'rss', '~> 0.3'
36
36
  dependency 'term-ansicolor', '~> 1.11'
37
37
  dependency 'redis', '~> 5.0'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.8
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
- OllamaChat::ServerSocket.send_to_server_socket(STDIN.read)
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
@@ -32,7 +32,7 @@ class OllamaChat::Chat
32
32
  include OllamaChat::ServerSocket
33
33
 
34
34
  def initialize(argv: ARGV.dup)
35
- @opts = go 'f:u:m:s:c:C:D:MEVh', argv
35
+ @opts = go 'f:u:m:s:c:C:D:MESVh', argv
36
36
  @opts[?h] and exit usage
37
37
  @opts[?V] and exit version
38
38
  @ollama_chat_config = OllamaChat::OllamaChatConfig.new(@opts[?f])
@@ -67,7 +67,7 @@ class OllamaChat::Chat
67
67
  @current_voice = config.voice.default
68
68
  @images = []
69
69
  init_chat_history
70
- init_server_socket
70
+ @opts[?S] and init_server_socket
71
71
  rescue ComplexConfig::AttributeMissing, ComplexConfig::ConfigurationSyntaxError => e
72
72
  fix_config(e)
73
73
  end
@@ -106,232 +106,264 @@ 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
- input_prompt = bold { color(172) { message_type(@images) + " user" } } + bold { "> " }
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
- case content
126
- when %r(^/copy$)
127
- copy_to_clipboard
128
- next
129
- when %r(^/paste$)
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
- [ content, Documentrix::Utils::Tags.new ]
366
+ [ content, Documentrix::Utils::Tags.new(valid_tag: /\A#*([\w\]\[]+)/) ]
335
367
  end
336
368
 
337
369
  if embedding.on? && content
@@ -102,6 +102,7 @@ module OllamaChat::Information
102
102
  -D DOCUMENT load document and add to embeddings collection (multiple)
103
103
  -M use (empty) MemoryCache for this chat session
104
104
  -E disable embeddings for this chat session
105
+ -S open a socket to receive input from ollama_chat_send
105
106
  -V display the current version number and quit
106
107
  -h this help
107
108
 
@@ -113,10 +113,10 @@ module OllamaChat::Parsing
113
113
 
114
114
  def parse_content(content, images)
115
115
  images.clear
116
- tags = Documentrix::Utils::Tags.new
116
+ tags = Documentrix::Utils::Tags.new valid_tag: /\A#*([\w\]\[]+)/
117
117
 
118
118
  contents = [ content ]
119
- content.scan(%r((https?://\S+)|(#\S+)|(?:file://)?(\S*\/\S+))).each do |url, tag, file|
119
+ content.scan(%r((https?://\S+)|(?<![a-zA-Z\d])#+([\w\]\[]+)|(?:file://)?(\S*\/\S+))).each do |url, tag, file|
120
120
  case
121
121
  when tag
122
122
  tags.add(tag)
@@ -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
@@ -1,6 +1,6 @@
1
1
  module OllamaChat
2
2
  # OllamaChat version
3
- VERSION = '0.0.8'
3
+ VERSION = '0.0.10'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
data/ollama_chat.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: ollama_chat 0.0.8 ruby lib
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.8".freeze
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]
@@ -33,7 +33,7 @@ Gem::Specification.new do |s|
33
33
  s.add_development_dependency(%q<simplecov>.freeze, [">= 0".freeze])
34
34
  s.add_runtime_dependency(%q<excon>.freeze, ["~> 1.0".freeze])
35
35
  s.add_runtime_dependency(%q<ollama-ruby>.freeze, ["~> 1.0".freeze])
36
- s.add_runtime_dependency(%q<documentrix>.freeze, ["~> 0.0".freeze])
36
+ s.add_runtime_dependency(%q<documentrix>.freeze, ["~> 0.0".freeze, ">= 0.0.2".freeze])
37
37
  s.add_runtime_dependency(%q<rss>.freeze, ["~> 0.3".freeze])
38
38
  s.add_runtime_dependency(%q<term-ansicolor>.freeze, ["~> 1.11".freeze])
39
39
  s.add_runtime_dependency(%q<redis>.freeze, ["~> 5.0".freeze])
@@ -6,43 +6,211 @@ RSpec.describe OllamaChat::Chat do
6
6
  end
7
7
 
8
8
  let :chat do
9
- OllamaChat::Chat.new argv: argv
9
+ OllamaChat::Chat.new(argv: argv).expose
10
10
  end
11
11
 
12
- connect_to_ollama_server(instantiate: false)
12
+ describe 'instantiation' do
13
+ connect_to_ollama_server(instantiate: false)
13
14
 
14
- it 'can be instantiated' do
15
- expect(chat).to be_a described_class
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.send(:chat_history_filename)).to_not be_nil
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.send(:chat_history_filename),
191
+ chat.chat_history_filename,
26
192
  kind_of(String)
27
193
  )
28
- chat.send(:save_history)
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.send(:chat_history_filename)).
198
+ expect(File).to receive(:exist?).with(chat.chat_history_filename).
33
199
  and_return true
34
- expect(File).to receive(:open).with(chat.send(:chat_history_filename), ?r)
35
- chat.send(:init_chat_history)
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.send(:clear_history)
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ollama_chat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -142,6 +142,9 @@ dependencies:
142
142
  - - "~>"
143
143
  - !ruby/object:Gem::Version
144
144
  version: '0.0'
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: 0.0.2
145
148
  type: :runtime
146
149
  prerelease: false
147
150
  version_requirements: !ruby/object:Gem::Requirement
@@ -149,6 +152,9 @@ dependencies:
149
152
  - - "~>"
150
153
  - !ruby/object:Gem::Version
151
154
  version: '0.0'
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: 0.0.2
152
158
  - !ruby/object:Gem::Dependency
153
159
  name: rss
154
160
  requirement: !ruby/object:Gem::Requirement