ollama-ruby 0.9.0 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0b3ea2bd67e3f4fa2ca4da88a25cb869fc81e35578f1e7fd97ba43e84fc2278
4
- data.tar.gz: 626c534bd2fd0580ab0520e8b70587489edbc963e179be0f800ee63235690b33
3
+ metadata.gz: d2a82c4fc1db4e92e628d0e3a84525273f2ee3bd994f34185c742f0dab1d9b78
4
+ data.tar.gz: b77caa026511e2bc49bdf3d2e3782cc6a1edd1f60161f1301efbc927dc2d3cba
5
5
  SHA512:
6
- metadata.gz: 149986d8144b1bc2c20771e245ead20f6e6bc07b46dbe26f8f355e0bc61eca146cc085a920dcbe3e3e08de65632693a35b6995c9dffef288aa929dd6b034de16
7
- data.tar.gz: 4214aa2911db561635011b8ea200074d15c74b93bf7abb3c8e77af9c40ac5d8994e908e2ade8e5f288c2919678e03ea5d84d07684a63ba1f8039de52b6091a86
6
+ metadata.gz: 6ea50f6c8fd275365b52b4c7047abc627079804dd7db61120dbbcf2c6b3f3fddf1946a46508fbb0bc26d0d7284d3b23f4f934b7c418bc91799417a5c2b72332c
7
+ data.tar.gz: cef39db9d4eb0ee160c53c4e522a158942c3532203f46d50ee88af2c1f3f36aac0030087e71864045c4979018be4891329155ad0e8863c4e8c52080ba3e28f3f
data/CHANGES.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changes
2
2
 
3
+ ## 2024-10-20 v0.9.2
4
+
5
+ * Added SourceParsing module and update parse_source method to use it:
6
+ + Added `SourceParsing` module with `parse_source` method that handles
7
+ different file types (e.g. HTML, XML, CSV, RSS)
8
+ + Added `parse_csv` method to `SourceParsing` module
9
+ + Updated `parse_source` method in main file to include new functionality
10
+ * Add colorize texts spec for Ollama::Utils::ColorizeTexts to test its
11
+ functionality.
12
+ * Added test for expected output of `documents.tags`
13
+ * Add test for empty Ollama options:
14
+ + Added test case for `Ollama::Options` being empty
15
+ * Display (embedding) model options in info output
16
+ * Only show `collection_stats` if embedding is performed
17
+ * Add empty? method to DTO class:
18
+ + Added `empty?` method to `Ollama::DTO` class using `to_hash.empty?`
19
+ + Method is used to check if the object's hash representation is empty.
20
+
21
+ ## 2024-10-19 v0.9.1
22
+
23
+ * Fixing string interpolation in `import_source` method:
24
+ + Changed result to use `#{}` instead of `%{}` for string interpolation
25
+ * Move `pull_model_unless_present` method:
26
+ + Moved the method before other methods
27
+ * Moved Switch classes and `setup_switches` method into Switches module:
28
+ + Moved the classes and method into a new module
29
+ * Utils::Fetcher: Normalize URL in fetcher utility:
30
+ + Added `normalize_url` method to class Ollama::Utils::Fetcher
31
+ + Normalizes URL by decoding URI components, removing anchors, and escaping special characters
32
+ + `excon` method now calls `normalize_url` on the provided URL
33
+ + Added specs for `normalize_url` in `fetcher_spec.rb`
34
+ * Remove Ollama top level Namespace b/c we include it from `ollama_chat`:
35
+ + Removed the top-level namespace
36
+
3
37
  ## 2024-10-18 v0.9.0
4
38
 
5
39
  * Add document policy chooser and modify embedding/importing/summarizing
data/bin/ollama_chat CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  require 'ollama'
4
4
  include Ollama
5
+ include Tins::GO
5
6
  require 'term/ansicolor'
6
7
  include Term::ANSIColor
7
- include Tins::GO
8
8
  require 'reline'
9
9
  require 'reverse_markdown'
10
10
  require 'complex_config'
@@ -108,7 +108,7 @@ class OllamaChatConfig
108
108
  end
109
109
 
110
110
  class FollowChat
111
- include Ollama::Handlers::Concern
111
+ include Handlers::Concern
112
112
  include Term::ANSIColor
113
113
 
114
114
  def initialize(messages:, markdown: false, voice: nil, output: $stdout)
@@ -164,114 +164,142 @@ class FollowChat
164
164
  end
165
165
  end
166
166
 
167
- module CheckSwitch
168
- extend Tins::Concern
167
+ module Switches
168
+ module CheckSwitch
169
+ extend Tins::Concern
169
170
 
170
- included do
171
- alias_method :on?, :value
172
- end
171
+ included do
172
+ alias_method :on?, :value
173
+ end
173
174
 
174
- def off?
175
- !on?
176
- end
175
+ def off?
176
+ !on?
177
+ end
177
178
 
178
- def show
179
- puts @msg[value]
179
+ def show
180
+ puts @msg[value]
181
+ end
180
182
  end
181
- end
182
183
 
183
- class Switch
184
- def initialize(name, msg:, config: $config)
185
- @value = [ false, true ].include?(config) ? config : !!config.send("#{name}?")
186
- @msg = msg
187
- end
184
+ class Switch
185
+ def initialize(name, msg:, config: $config)
186
+ @value = [ false, true ].include?(config) ? config : !!config.send("#{name}?")
187
+ @msg = msg
188
+ end
188
189
 
189
- attr_reader :value
190
+ attr_reader :value
190
191
 
191
- def set(value, show: false)
192
- @value = !!value
193
- show && self.show
194
- end
192
+ def set(value, show: false)
193
+ @value = !!value
194
+ show && self.show
195
+ end
196
+
197
+ def toggle(show: true)
198
+ @value = !@value
199
+ show && self.show
200
+ end
195
201
 
196
- def toggle(show: true)
197
- @value = !@value
198
- show && self.show
202
+ include CheckSwitch
199
203
  end
200
204
 
201
- include CheckSwitch
202
- end
205
+ class CombinedSwitch
206
+ def initialize(value:, msg:)
207
+ @value = value
208
+ @msg = msg
209
+ end
203
210
 
204
- class CombinedSwitch
205
- def initialize(value:, msg:)
206
- @value = value
207
- @msg = msg
208
- end
211
+ def value
212
+ @value.()
213
+ end
209
214
 
210
- def value
211
- @value.()
215
+ include CheckSwitch
212
216
  end
213
217
 
214
- include CheckSwitch
215
- end
218
+ def setup_switches
219
+ $markdown = Switch.new(
220
+ :markdown,
221
+ msg: {
222
+ true => "Using #{italic{'ANSI'}} markdown to output content.",
223
+ false => "Using plaintext for outputting content.",
224
+ }
225
+ )
216
226
 
217
- def setup_switches
218
- $markdown = Switch.new(
219
- :markdown,
220
- msg: {
221
- true => "Using #{italic{'ANSI'}} markdown to output content.",
222
- false => "Using plaintext for outputting content.",
223
- }
224
- )
227
+ $stream = Switch.new(
228
+ :stream,
229
+ msg: {
230
+ true => "Streaming enabled.",
231
+ false => "Streaming disabled.",
232
+ }
233
+ )
225
234
 
226
- $stream = Switch.new(
227
- :stream,
228
- msg: {
229
- true => "Streaming enabled.",
230
- false => "Streaming disabled.",
231
- }
232
- )
235
+ $voice = Switch.new(
236
+ :stream,
237
+ msg: {
238
+ true => "Voice output enabled.",
239
+ false => "Voice output disabled.",
240
+ },
241
+ config: $config.voice
242
+ )
233
243
 
234
- $voice = Switch.new(
235
- :stream,
236
- msg: {
237
- true => "Voice output enabled.",
238
- false => "Voice output disabled.",
239
- },
240
- config: $config.voice
241
- )
244
+ $embedding_enabled = Switch.new(
245
+ :embedding_enabled,
246
+ msg: {
247
+ true => "Embedding enabled.",
248
+ false => "Embedding disabled.",
249
+ }
250
+ )
242
251
 
243
- $embedding_enabled = Switch.new(
244
- :embedding_enabled,
245
- msg: {
246
- true => "Embedding enabled.",
247
- false => "Embedding disabled.",
248
- }
249
- )
252
+ $embedding_paused = Switch.new(
253
+ :embedding_paused,
254
+ msg: {
255
+ true => "Embedding paused.",
256
+ false => "Embedding resumed.",
257
+ }
258
+ )
250
259
 
251
- $embedding_paused = Switch.new(
252
- :embedding_paused,
253
- msg: {
254
- true => "Embedding paused.",
255
- false => "Embedding resumed.",
256
- }
257
- )
260
+ $embedding = CombinedSwitch.new(
261
+ value: -> { $embedding_enabled.on? && $embedding_paused.off? },
262
+ msg: {
263
+ true => "Embedding is currently performed.",
264
+ false => "Embedding is currently not performed.",
265
+ }
266
+ )
258
267
 
259
- $embedding = CombinedSwitch.new(
260
- value: -> { $embedding_enabled.on? && $embedding_paused.off? },
261
- msg: {
262
- true => "Embedding is currently performed.",
263
- false => "Embedding is currently not performed.",
264
- }
265
- )
268
+ $location = Switch.new(
269
+ :location,
270
+ msg: {
271
+ true => "Location and localtime enabled.",
272
+ false => "Location and localtime disabled.",
273
+ },
274
+ config: $config.location.enabled
275
+ )
276
+ end
277
+ end
278
+ include Switches
266
279
 
267
- $location = Switch.new(
268
- :location,
269
- msg: {
270
- true => "Location and localtime enabled.",
271
- false => "Location and localtime disabled.",
272
- },
273
- config: $config.location.enabled
274
- )
280
+ def pull_model_unless_present(model, options, retried = false)
281
+ ollama.show(name: model) { |response|
282
+ puts "Model #{bold{model}} with architecture "\
283
+ "#{response.model_info['general.architecture']} found."
284
+ if system = response.system
285
+ puts "Configured model system prompt is:\n#{italic { system }}"
286
+ return system
287
+ else
288
+ return
289
+ end
290
+ }
291
+ rescue Errors::NotFoundError
292
+ puts "Model #{bold{model}} not found locally, attempting to pull it from remote now…"
293
+ ollama.pull(name: model)
294
+ if retried
295
+ exit 1
296
+ else
297
+ retried = true
298
+ retry
299
+ end
300
+ rescue Errors::Error => e
301
+ warn "Caught #{e.class} while pulling model: #{e} => Exiting."
302
+ exit 1
275
303
  end
276
304
 
277
305
  def search_web(query, n = nil)
@@ -282,7 +310,7 @@ def search_web(query, n = nil)
282
310
  n < 1 and n = 1
283
311
  query = URI.encode_uri_component(query)
284
312
  url = "https://www.duckduckgo.com/html/?q=#{query}"
285
- Ollama::Utils::Fetcher.get(url, debug: $config.debug) do |tmp|
313
+ Utils::Fetcher.get(url, debug: $config.debug) do |tmp|
286
314
  result = []
287
315
  doc = Nokogiri::HTML(tmp)
288
316
  doc.css('.results_links').each do |link|
@@ -303,38 +331,13 @@ def search_web(query, n = nil)
303
331
  end
304
332
  end
305
333
 
306
- def pull_model_unless_present(model, options, retried = false)
307
- ollama.show(name: model) { |response|
308
- puts "Model #{bold{model}} with architecture "\
309
- "#{response.model_info['general.architecture']} found."
310
- if system = response.system
311
- puts "Configured model system prompt is:\n#{italic { system }}"
312
- return system
313
- else
314
- return
315
- end
316
- }
317
- rescue Errors::NotFoundError
318
- puts "Model #{bold{model}} not found locally, attempting to pull it from remote now…"
319
- ollama.pull(name: model)
320
- if retried
321
- exit 1
322
- else
323
- retried = true
324
- retry
325
- end
326
- rescue Errors::Error => e
327
- warn "Caught #{e.class} while pulling model: #{e} => Exiting."
328
- exit 1
329
- end
330
-
331
334
  def load_conversation(filename)
332
335
  unless File.exist?(filename)
333
336
  puts "File #{filename} doesn't exist. Choose another filename."
334
337
  return
335
338
  end
336
339
  File.open(filename, 'r') do |output|
337
- return JSON(output.read).map { Ollama::Message.from_hash(_1) }
340
+ return JSON(output.read).map { Message.from_hash(_1) }
338
341
  end
339
342
  end
340
343
 
@@ -372,113 +375,120 @@ def list_conversation(messages, last = nil)
372
375
  end
373
376
  end
374
377
 
375
- def reverse_markdown(html)
376
- ReverseMarkdown.convert(
377
- html,
378
- unknown_tags: :bypass,
379
- github_flavored: true,
380
- tag_border: ''
381
- )
382
- end
378
+ module SourceParsing
379
+ def parse_source(source_io)
380
+ case source_io&.content_type
381
+ when 'text/html'
382
+ reverse_markdown(source_io.read)
383
+ when 'text/xml'
384
+ if source_io.readline =~ %r(^\s*<rss\s)
385
+ source_io.rewind
386
+ return parse_rss(source_io)
387
+ end
388
+ source_io.rewind
389
+ source_io.read
390
+ when 'text/csv' # TODO
391
+ parse_csv(source_io)
392
+ when 'application/rss+xml'
393
+ parse_rss(source_io)
394
+ when 'application/atom+xml'
395
+ parse_atom(source_io)
396
+ when 'application/postscript'
397
+ ps_read(source_io)
398
+ when 'application/pdf'
399
+ pdf_read(source_io)
400
+ when %r(\Aapplication/(json|ld\+json|x-ruby|x-perl|x-gawk|x-python|x-javascript|x-c?sh|x-dosexec|x-shellscript|x-tex|x-latex|x-lyx|x-bibtex)), %r(\Atext/), nil
401
+ source_io.read
402
+ else
403
+ STDERR.puts "Cannot embed #{source_io&.content_type} document."
404
+ return
405
+ end
406
+ end
383
407
 
384
- def parse_rss(source_io)
385
- feed = RSS::Parser.parse(source_io, false, false)
386
- title = <<~EOT
387
- # #{feed&.channel&.title}
408
+ def parse_csv(source_io)
409
+ result = +''
410
+ CSV.table(File.new(source_io), col_sep: ?,).each do |row|
411
+ next if row.fields.select(&:present?).size == 0
412
+ result << row.map { |pair|
413
+ pair.compact.map { _1.to_s.strip } * ': ' if pair.last.present?
414
+ }.select(&:present?).map { _1.prepend(' ') } * ?\n
415
+ result << "\n\n"
416
+ end
417
+ result
418
+ end
388
419
 
389
- EOT
390
- feed.items.inject(title) do |text, item|
391
- text << <<~EOT
392
- ## [#{item&.title}](#{item&.link})
420
+ def parse_rss(source_io)
421
+ feed = RSS::Parser.parse(source_io, false, false)
422
+ title = <<~EOT
423
+ # #{feed&.channel&.title}
393
424
 
394
- updated on #{item&.pubDate}
425
+ EOT
426
+ feed.items.inject(title) do |text, item|
427
+ text << <<~EOT
428
+ ## [#{item&.title}](#{item&.link})
395
429
 
396
- #{reverse_markdown(item&.description)}
430
+ updated on #{item&.pubDate}
397
431
 
398
- EOT
432
+ #{reverse_markdown(item&.description)}
433
+
434
+ EOT
435
+ end
399
436
  end
400
- end
401
437
 
402
- def parse_atom(source_io)
403
- feed = RSS::Parser.parse(source_io, false, false)
404
- title = <<~EOT
405
- # #{feed.title.content}
438
+ def parse_atom(source_io)
439
+ feed = RSS::Parser.parse(source_io, false, false)
440
+ title = <<~EOT
441
+ # #{feed.title.content}
406
442
 
407
- EOT
408
- feed.items.inject(title) do |text, item|
409
- text << <<~EOT
410
- ## [#{item&.title&.content}](#{item&.link&.href})
443
+ EOT
444
+ feed.items.inject(title) do |text, item|
445
+ text << <<~EOT
446
+ ## [#{item&.title&.content}](#{item&.link&.href})
411
447
 
412
- updated on #{item&.updated&.content}
448
+ updated on #{item&.updated&.content}
413
449
 
414
- #{reverse_markdown(item&.content&.content)}
450
+ #{reverse_markdown(item&.content&.content)}
415
451
 
416
- EOT
452
+ EOT
453
+ end
417
454
  end
418
- end
419
455
 
420
- def pdf_read(io)
421
- reader = PDF::Reader.new(io)
422
- reader.pages.inject(+'') { |result, page| result << page.text }
423
- end
456
+ def pdf_read(io)
457
+ reader = PDF::Reader.new(io)
458
+ reader.pages.inject(+'') { |result, page| result << page.text }
459
+ end
424
460
 
425
- def ps_read(io)
426
- gs = `which gs`.chomp
427
- if gs.present?
428
- Tempfile.create do |tmp|
429
- IO.popen("#{gs} -q -sDEVICE=pdfwrite -sOutputFile=#{tmp.path} -", 'wb') do |gs_io|
430
- until io.eof?
431
- buffer = io.read(1 << 17)
432
- IO.select(nil, [ gs_io ], nil)
433
- gs_io.write buffer
434
- end
435
- gs_io.close
436
- File.open(tmp.path, 'rb') do |pdf|
437
- pdf_read(pdf)
461
+ def ps_read(io)
462
+ gs = `which gs`.chomp
463
+ if gs.present?
464
+ Tempfile.create do |tmp|
465
+ IO.popen("#{gs} -q -sDEVICE=pdfwrite -sOutputFile=#{tmp.path} -", 'wb') do |gs_io|
466
+ until io.eof?
467
+ buffer = io.read(1 << 17)
468
+ IO.select(nil, [ gs_io ], nil)
469
+ gs_io.write buffer
470
+ end
471
+ gs_io.close
472
+ File.open(tmp.path, 'rb') do |pdf|
473
+ pdf_read(pdf)
474
+ end
438
475
  end
439
476
  end
477
+ else
478
+ STDERR.puts "Cannot convert #{io&.content_type} whith ghostscript, gs not in path."
440
479
  end
441
- else
442
- STDERR.puts "Cannot convert #{io&.content_type} whith ghostscript, gs not in path."
443
480
  end
444
- end
445
481
 
446
- def parse_source(source_io)
447
- case source_io&.content_type
448
- when 'text/html'
449
- reverse_markdown(source_io.read)
450
- when 'text/xml'
451
- if source_io.readline =~ %r(^\s*<rss\s)
452
- source_io.rewind
453
- return parse_rss(source_io)
454
- end
455
- source_io.rewind
456
- source_io.read
457
- when 'text/csv'
458
- result = +''
459
- CSV.table(File.new(source_io), col_sep: ?,).each do |row|
460
- next if row.fields.select(&:present?).size == 0
461
- result << row.map { |pair|
462
- pair.compact.map { _1.to_s.strip } * ': ' if pair.last.present?
463
- }.select(&:present?).map { _1.prepend(' ') } * ?\n
464
- result << "\n\n"
465
- end
466
- result
467
- when 'application/rss+xml'
468
- parse_rss(source_io)
469
- when 'application/atom+xml'
470
- parse_atom(source_io)
471
- when 'application/postscript'
472
- ps_read(source_io)
473
- when 'application/pdf'
474
- pdf_read(source_io)
475
- when %r(\Aapplication/(json|ld\+json|x-ruby|x-perl|x-gawk|x-python|x-javascript|x-c?sh|x-dosexec|x-shellscript|x-tex|x-latex|x-lyx|x-bibtex)), %r(\Atext/), nil
476
- source_io.read
477
- else
478
- STDERR.puts "Cannot embed #{source_io&.content_type} document."
479
- return
482
+ def reverse_markdown(html)
483
+ ReverseMarkdown.convert(
484
+ html,
485
+ unknown_tags: :bypass,
486
+ github_flavored: true,
487
+ tag_border: ''
488
+ )
480
489
  end
481
490
  end
491
+ include SourceParsing
482
492
 
483
493
  def http_options(url)
484
494
  options = {}
@@ -504,7 +514,7 @@ def fetch_source(source, &block)
504
514
  source,
505
515
  cache: $cache,
506
516
  debug: $config.debug,
507
- http_options: http_options(source)
517
+ http_options: http_options(Utils::Fetcher.normalize_url(source))
508
518
  ) do |tmp|
509
519
  block.(tmp)
510
520
  end
@@ -518,7 +528,7 @@ def fetch_source(source, &block)
518
528
  raise "invalid source"
519
529
  end
520
530
  rescue => e
521
- STDERR.puts "Cannot fetch source #{source.to_s.inspect}: #{e}\n#{e.backtrace * ?\n}"
531
+ STDERR.puts "Cannot fetch source #{source.to_s.inspect}: #{e.class} #{e}\n#{e.backtrace * ?\n}"
522
532
  end
523
533
 
524
534
  def add_image(images, source_io, source)
@@ -530,7 +540,8 @@ end
530
540
  def import_source(source_io, source)
531
541
  source = source.to_s
532
542
  puts "Importing #{italic { source_io&.content_type }} document #{source.inspect} now."
533
- "Imported #{source.inspect}:\n%s\n\n" % parse_source(source_io)
543
+ source_content = parse_source(source_io)
544
+ "Imported #{source.inspect}:\n#{source_content}\n\n"
534
545
  end
535
546
 
536
547
  def import(source)
@@ -572,17 +583,17 @@ def embed_source(source_io, source, count: nil)
572
583
  inputs = nil
573
584
  case splitter_config.name
574
585
  when 'Character'
575
- splitter = Ollama::Documents::Splitters::Character.new(
586
+ splitter = Documents::Splitters::Character.new(
576
587
  chunk_size: splitter_config.chunk_size,
577
588
  )
578
589
  inputs = splitter.split(text)
579
590
  when 'RecursiveCharacter'
580
- splitter = Ollama::Documents::Splitters::RecursiveCharacter.new(
591
+ splitter = Documents::Splitters::RecursiveCharacter.new(
581
592
  chunk_size: splitter_config.chunk_size,
582
593
  )
583
594
  inputs = splitter.split(text)
584
595
  when 'Semantic'
585
- splitter = Ollama::Documents::Splitters::Semantic.new(
596
+ splitter = Documents::Splitters::Semantic.new(
586
597
  ollama:, model: $config.embedding.model.name,
587
598
  chunk_size: splitter_config.chunk_size,
588
599
  )
@@ -597,7 +608,7 @@ def embed_source(source_io, source, count: nil)
597
608
  inputs or return
598
609
  source = source.to_s
599
610
  if source.start_with?(?!)
600
- source = Ollama::Utils::Width.truncate(
611
+ source = Utils::Width.truncate(
601
612
  source[1..-1].gsub(/\W+/, ?_),
602
613
  length: 10
603
614
  )
@@ -662,7 +673,7 @@ end
662
673
  def choose_model(cli_model, current_model)
663
674
  models = ollama.tags.models.map(&:name).sort
664
675
  model = if cli_model == ''
665
- Ollama::Utils::Chooser.choose(models) || current_model
676
+ Utils::Chooser.choose(models) || current_model
666
677
  else
667
678
  cli_model || current_model
668
679
  end
@@ -679,7 +690,7 @@ def choose_collection(current_collection)
679
690
  collections = [ current_collection ] + $documents.collections
680
691
  collections = collections.compact.map(&:to_s).uniq.sort
681
692
  collections.unshift('[EXIT]').unshift('[NEW]')
682
- collection = Ollama::Utils::Chooser.choose(collections) || current_collection
693
+ collection = Utils::Chooser.choose(collections) || current_collection
683
694
  case collection
684
695
  when '[NEW]'
685
696
  $documents.collection = ask?(prompt: "Enter name of the new collection: ")
@@ -703,7 +714,7 @@ def choose_document_policy
703
714
  policies.first
704
715
  end
705
716
  policies.unshift('[EXIT]')
706
- policy = Ollama::Utils::Chooser.choose(policies)
717
+ policy = Utils::Chooser.choose(policies)
707
718
  case policy
708
719
  when nil, '[EXIT]'
709
720
  puts "Exiting chooser."
@@ -722,7 +733,6 @@ def collection_stats
722
733
  puts <<~EOT
723
734
  Current Collection
724
735
  Name: #{bold{$documents.collection}}
725
- Embedding model: #{bold{$embedding_model}}
726
736
  #Embeddings: #{$documents.size}
727
737
  #Tags: #{$documents.tags.size}
728
738
  Tags: #{$documents.tags}
@@ -733,19 +743,19 @@ end
733
743
 
734
744
  def configure_cache
735
745
  if $opts[?M]
736
- Ollama::Documents::MemoryCache
746
+ Documents::MemoryCache
737
747
  else
738
748
  Object.const_get($config.cache)
739
749
  end
740
750
  rescue => e
741
751
  STDERR.puts "Caught #{e.class}: #{e} => Falling back to MemoryCache."
742
- Ollama::Documents::MemoryCache
752
+ Documents::MemoryCache
743
753
  end
744
754
 
745
755
  def show_system_prompt
746
756
  puts <<~EOT
747
757
  Configured system prompt is:
748
- #{Ollama::Utils::ANSIMarkdown.parse($system.to_s).gsub(/\n+\z/, '').full? || 'n/a'}
758
+ #{Utils::ANSIMarkdown.parse($system.to_s).gsub(/\n+\z/, '').full? || 'n/a'}
749
759
  EOT
750
760
  end
751
761
 
@@ -769,7 +779,7 @@ end
769
779
 
770
780
  def change_system_prompt(messages, default)
771
781
  prompts = $config.system_prompts.attribute_names.compact
772
- chosen = Ollama::Utils::Chooser.choose(prompts)
782
+ chosen = Utils::Chooser.choose(prompts)
773
783
  system = if chosen
774
784
  $config.system_prompts.send(chosen)
775
785
  else
@@ -779,16 +789,23 @@ def change_system_prompt(messages, default)
779
789
  end
780
790
 
781
791
  def change_voice
782
- chosen = Ollama::Utils::Chooser.choose($config.voice.list)
792
+ chosen = Utils::Chooser.choose($config.voice.list)
783
793
  $current_voice = chosen.full? || $config.voice.default
784
794
  end
785
795
 
786
796
  def info
787
797
  puts "Current model is #{bold{$model}}."
788
- collection_stats
798
+ if $model_options.present?
799
+ puts " Options: #{JSON.pretty_generate($model_options).gsub(/(?<!\A)^/, ' ')}"
800
+ end
789
801
  $embedding.show
790
802
  if $embedding.on?
803
+ puts "Embedding model is #{bold{$embedding_model}}"
804
+ if $embedding_model_options.present?
805
+ puts " Options: #{JSON.pretty_generate($embedding_model_options).gsub(/(?<!\A)^/, ' ')}"
806
+ end
791
807
  puts "Text splitter is #{bold{$config.embedding.splitter.name}}."
808
+ collection_stats
792
809
  end
793
810
  puts "Documents database cache is #{$documents.nil? ? 'n/a' : bold{$documents.cache.class}}"
794
811
  $markdown.show
@@ -891,12 +908,12 @@ $opts[?h] and usage
891
908
  $opts[?V] and version
892
909
 
893
910
  base_url = $opts[?u] || $config.url
894
- $ollama = Client.new(base_url:, debug: $config.debug)
911
+ $ollama = Client.new(base_url:, debug: $config.debug)
895
912
 
896
913
  $document_policy = $config.document_policy
897
914
  $model = choose_model($opts[?m], $config.model.name)
898
- options = Options[$config.model.options]
899
- model_system = pull_model_unless_present($model, options)
915
+ $model_options = Options[$config.model.options]
916
+ model_system = pull_model_unless_present($model, $model_options)
900
917
  messages = []
901
918
  $embedding_enabled.set($config.embedding.enabled && !$opts[?E])
902
919
 
@@ -907,15 +924,15 @@ else
907
924
  if $opts[?s] == ??
908
925
  change_system_prompt(messages, default)
909
926
  else
910
- system = Ollama::Utils::FileArgument.get_file_argument($opts[?s], default:)
927
+ system = Utils::FileArgument.get_file_argument($opts[?s], default:)
911
928
  system.present? and set_system_prompt(messages, system)
912
929
  end
913
930
  end
914
931
 
915
932
  if $embedding.on?
916
933
  $embedding_model = $config.embedding.model.name
917
- embedding_model_options = Options[$config.embedding.model.options]
918
- pull_model_unless_present($embedding_model, embedding_model_options)
934
+ $embedding_model_options = Options[$config.embedding.model.options]
935
+ pull_model_unless_present($embedding_model, $embedding_model_options)
919
936
  collection = $opts[?C] || $config.embedding.collection
920
937
  $documents = Documents.new(
921
938
  ollama:,
@@ -957,7 +974,7 @@ else
957
974
  end
958
975
 
959
976
  if redis_expiring_url = $config.redis.expiring.url?
960
- $cache = Ollama::Documents::RedisCache.new(
977
+ $cache = Documents::RedisCache.new(
961
978
  prefix: 'Expiring-',
962
979
  url: redis_expiring_url,
963
980
  ex: $config.redis.expiring.ex,
@@ -1051,7 +1068,7 @@ loop do
1051
1068
  when 'clear'
1052
1069
  loop do
1053
1070
  tags = $documents.tags.add('[EXIT]').add('[ALL]')
1054
- tag = Ollama::Utils::Chooser.choose(tags, prompt: 'Clear? %s')
1071
+ tag = Utils::Chooser.choose(tags, prompt: 'Clear? %s')
1055
1072
  case tag
1056
1073
  when nil, '[EXIT]'
1057
1074
  puts "Exiting chooser."
@@ -1154,7 +1171,7 @@ loop do
1154
1171
  messages << Message.new(role: 'user', content:, images: images.dup)
1155
1172
  images.clear
1156
1173
  handler = FollowChat.new(messages:, markdown: $markdown.on?, voice: ($current_voice if $voice.on?))
1157
- ollama.chat(model: $model, messages:, options:, stream: $stream.on?, &handler)
1174
+ ollama.chat(model: $model, messages:, options: $model_options, stream: $stream.on?, &handler)
1158
1175
 
1159
1176
  if $embedding.on? && !records.empty?
1160
1177
  puts "", records.map { |record|
data/lib/ollama/dto.rb CHANGED
@@ -33,6 +33,10 @@ module Ollama::DTO
33
33
 
34
34
  alias to_hash as_json
35
35
 
36
+ def empty?
37
+ to_hash.empty?
38
+ end
39
+
36
40
  def to_json(*)
37
41
  as_json.to_json(*)
38
42
  end
@@ -38,6 +38,13 @@ class Ollama::Utils::Fetcher
38
38
  end
39
39
  end
40
40
 
41
+ def self.normalize_url(url)
42
+ url = url.to_s
43
+ url = URI.decode_uri_component(url)
44
+ url = url.sub(/#.*/, '')
45
+ URI::Parser.new.escape(url).to_s
46
+ end
47
+
41
48
  def self.read(filename, &block)
42
49
  if File.exist?(filename)
43
50
  File.open(filename) do |file|
@@ -80,6 +87,7 @@ class Ollama::Utils::Fetcher
80
87
  private
81
88
 
82
89
  def excon(url, **options)
90
+ url = self.class.normalize_url(url)
83
91
  Excon.new(url, options.merge(@http_options))
84
92
  end
85
93
 
@@ -1,6 +1,6 @@
1
1
  module Ollama
2
2
  # Ollama version
3
- VERSION = '0.9.0'
3
+ VERSION = '0.9.2'
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-ruby.gemspec CHANGED
@@ -1,26 +1,26 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: ollama-ruby 0.9.0 ruby lib
2
+ # stub: ollama-ruby 0.9.2 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "ollama-ruby".freeze
6
- s.version = "0.9.0".freeze
6
+ s.version = "0.9.2".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]
10
10
  s.authors = ["Florian Frank".freeze]
11
- s.date = "2024-10-18"
11
+ s.date = "2024-10-20"
12
12
  s.description = "Library that allows interacting with the Ollama API".freeze
13
13
  s.email = "flori@ping.de".freeze
14
14
  s.executables = ["ollama_console".freeze, "ollama_chat".freeze, "ollama_update".freeze, "ollama_cli".freeze]
15
15
  s.extra_rdoc_files = ["README.md".freeze, "lib/ollama.rb".freeze, "lib/ollama/client.rb".freeze, "lib/ollama/client/command.rb".freeze, "lib/ollama/client/doc.rb".freeze, "lib/ollama/commands/chat.rb".freeze, "lib/ollama/commands/copy.rb".freeze, "lib/ollama/commands/create.rb".freeze, "lib/ollama/commands/delete.rb".freeze, "lib/ollama/commands/embed.rb".freeze, "lib/ollama/commands/embeddings.rb".freeze, "lib/ollama/commands/generate.rb".freeze, "lib/ollama/commands/ps.rb".freeze, "lib/ollama/commands/pull.rb".freeze, "lib/ollama/commands/push.rb".freeze, "lib/ollama/commands/show.rb".freeze, "lib/ollama/commands/tags.rb".freeze, "lib/ollama/documents.rb".freeze, "lib/ollama/documents/cache/common.rb".freeze, "lib/ollama/documents/cache/memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_backed_memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_cache.rb".freeze, "lib/ollama/documents/splitters/character.rb".freeze, "lib/ollama/documents/splitters/semantic.rb".freeze, "lib/ollama/dto.rb".freeze, "lib/ollama/errors.rb".freeze, "lib/ollama/handlers.rb".freeze, "lib/ollama/handlers/collector.rb".freeze, "lib/ollama/handlers/concern.rb".freeze, "lib/ollama/handlers/dump_json.rb".freeze, "lib/ollama/handlers/dump_yaml.rb".freeze, "lib/ollama/handlers/markdown.rb".freeze, "lib/ollama/handlers/nop.rb".freeze, "lib/ollama/handlers/print.rb".freeze, "lib/ollama/handlers/progress.rb".freeze, "lib/ollama/handlers/say.rb".freeze, "lib/ollama/handlers/single.rb".freeze, "lib/ollama/image.rb".freeze, "lib/ollama/message.rb".freeze, "lib/ollama/options.rb".freeze, "lib/ollama/response.rb".freeze, "lib/ollama/tool.rb".freeze, "lib/ollama/tool/function.rb".freeze, "lib/ollama/tool/function/parameters.rb".freeze, "lib/ollama/tool/function/parameters/property.rb".freeze, "lib/ollama/utils/ansi_markdown.rb".freeze, "lib/ollama/utils/cache_fetcher.rb".freeze, "lib/ollama/utils/chooser.rb".freeze, "lib/ollama/utils/colorize_texts.rb".freeze, "lib/ollama/utils/fetcher.rb".freeze, "lib/ollama/utils/file_argument.rb".freeze, "lib/ollama/utils/math.rb".freeze, "lib/ollama/utils/tags.rb".freeze, "lib/ollama/utils/width.rb".freeze, "lib/ollama/version.rb".freeze]
16
- s.files = [".envrc".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/ollama_chat".freeze, "bin/ollama_cli".freeze, "bin/ollama_console".freeze, "bin/ollama_update".freeze, "config/redis.conf".freeze, "docker-compose.yml".freeze, "lib/ollama.rb".freeze, "lib/ollama/client.rb".freeze, "lib/ollama/client/command.rb".freeze, "lib/ollama/client/doc.rb".freeze, "lib/ollama/commands/chat.rb".freeze, "lib/ollama/commands/copy.rb".freeze, "lib/ollama/commands/create.rb".freeze, "lib/ollama/commands/delete.rb".freeze, "lib/ollama/commands/embed.rb".freeze, "lib/ollama/commands/embeddings.rb".freeze, "lib/ollama/commands/generate.rb".freeze, "lib/ollama/commands/ps.rb".freeze, "lib/ollama/commands/pull.rb".freeze, "lib/ollama/commands/push.rb".freeze, "lib/ollama/commands/show.rb".freeze, "lib/ollama/commands/tags.rb".freeze, "lib/ollama/documents.rb".freeze, "lib/ollama/documents/cache/common.rb".freeze, "lib/ollama/documents/cache/memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_backed_memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_cache.rb".freeze, "lib/ollama/documents/splitters/character.rb".freeze, "lib/ollama/documents/splitters/semantic.rb".freeze, "lib/ollama/dto.rb".freeze, "lib/ollama/errors.rb".freeze, "lib/ollama/handlers.rb".freeze, "lib/ollama/handlers/collector.rb".freeze, "lib/ollama/handlers/concern.rb".freeze, "lib/ollama/handlers/dump_json.rb".freeze, "lib/ollama/handlers/dump_yaml.rb".freeze, "lib/ollama/handlers/markdown.rb".freeze, "lib/ollama/handlers/nop.rb".freeze, "lib/ollama/handlers/print.rb".freeze, "lib/ollama/handlers/progress.rb".freeze, "lib/ollama/handlers/say.rb".freeze, "lib/ollama/handlers/single.rb".freeze, "lib/ollama/image.rb".freeze, "lib/ollama/message.rb".freeze, "lib/ollama/options.rb".freeze, "lib/ollama/response.rb".freeze, "lib/ollama/tool.rb".freeze, "lib/ollama/tool/function.rb".freeze, "lib/ollama/tool/function/parameters.rb".freeze, "lib/ollama/tool/function/parameters/property.rb".freeze, "lib/ollama/utils/ansi_markdown.rb".freeze, "lib/ollama/utils/cache_fetcher.rb".freeze, "lib/ollama/utils/chooser.rb".freeze, "lib/ollama/utils/colorize_texts.rb".freeze, "lib/ollama/utils/fetcher.rb".freeze, "lib/ollama/utils/file_argument.rb".freeze, "lib/ollama/utils/math.rb".freeze, "lib/ollama/utils/tags.rb".freeze, "lib/ollama/utils/width.rb".freeze, "lib/ollama/version.rb".freeze, "ollama-ruby.gemspec".freeze, "spec/assets/embeddings.json".freeze, "spec/assets/kitten.jpg".freeze, "spec/assets/prompt.txt".freeze, "spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/chat_spec.rb".freeze, "spec/ollama/commands/copy_spec.rb".freeze, "spec/ollama/commands/create_spec.rb".freeze, "spec/ollama/commands/delete_spec.rb".freeze, "spec/ollama/commands/embed_spec.rb".freeze, "spec/ollama/commands/embeddings_spec.rb".freeze, "spec/ollama/commands/generate_spec.rb".freeze, "spec/ollama/commands/ps_spec.rb".freeze, "spec/ollama/commands/pull_spec.rb".freeze, "spec/ollama/commands/push_spec.rb".freeze, "spec/ollama/commands/show_spec.rb".freeze, "spec/ollama/commands/tags_spec.rb".freeze, "spec/ollama/documents/memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_backed_memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_cache_spec.rb".freeze, "spec/ollama/documents/splitters/character_spec.rb".freeze, "spec/ollama/documents/splitters/semantic_spec.rb".freeze, "spec/ollama/documents_spec.rb".freeze, "spec/ollama/handlers/collector_spec.rb".freeze, "spec/ollama/handlers/dump_json_spec.rb".freeze, "spec/ollama/handlers/dump_yaml_spec.rb".freeze, "spec/ollama/handlers/markdown_spec.rb".freeze, "spec/ollama/handlers/nop_spec.rb".freeze, "spec/ollama/handlers/print_spec.rb".freeze, "spec/ollama/handlers/progress_spec.rb".freeze, "spec/ollama/handlers/say_spec.rb".freeze, "spec/ollama/handlers/single_spec.rb".freeze, "spec/ollama/image_spec.rb".freeze, "spec/ollama/message_spec.rb".freeze, "spec/ollama/options_spec.rb".freeze, "spec/ollama/tool_spec.rb".freeze, "spec/ollama/utils/ansi_markdown_spec.rb".freeze, "spec/ollama/utils/cache_fetcher_spec.rb".freeze, "spec/ollama/utils/fetcher_spec.rb".freeze, "spec/ollama/utils/file_argument_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/ollama/utils/width_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tmp/.keep".freeze]
16
+ s.files = [".envrc".freeze, "CHANGES.md".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/ollama_chat".freeze, "bin/ollama_cli".freeze, "bin/ollama_console".freeze, "bin/ollama_update".freeze, "config/redis.conf".freeze, "docker-compose.yml".freeze, "lib/ollama.rb".freeze, "lib/ollama/client.rb".freeze, "lib/ollama/client/command.rb".freeze, "lib/ollama/client/doc.rb".freeze, "lib/ollama/commands/chat.rb".freeze, "lib/ollama/commands/copy.rb".freeze, "lib/ollama/commands/create.rb".freeze, "lib/ollama/commands/delete.rb".freeze, "lib/ollama/commands/embed.rb".freeze, "lib/ollama/commands/embeddings.rb".freeze, "lib/ollama/commands/generate.rb".freeze, "lib/ollama/commands/ps.rb".freeze, "lib/ollama/commands/pull.rb".freeze, "lib/ollama/commands/push.rb".freeze, "lib/ollama/commands/show.rb".freeze, "lib/ollama/commands/tags.rb".freeze, "lib/ollama/documents.rb".freeze, "lib/ollama/documents/cache/common.rb".freeze, "lib/ollama/documents/cache/memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_backed_memory_cache.rb".freeze, "lib/ollama/documents/cache/redis_cache.rb".freeze, "lib/ollama/documents/splitters/character.rb".freeze, "lib/ollama/documents/splitters/semantic.rb".freeze, "lib/ollama/dto.rb".freeze, "lib/ollama/errors.rb".freeze, "lib/ollama/handlers.rb".freeze, "lib/ollama/handlers/collector.rb".freeze, "lib/ollama/handlers/concern.rb".freeze, "lib/ollama/handlers/dump_json.rb".freeze, "lib/ollama/handlers/dump_yaml.rb".freeze, "lib/ollama/handlers/markdown.rb".freeze, "lib/ollama/handlers/nop.rb".freeze, "lib/ollama/handlers/print.rb".freeze, "lib/ollama/handlers/progress.rb".freeze, "lib/ollama/handlers/say.rb".freeze, "lib/ollama/handlers/single.rb".freeze, "lib/ollama/image.rb".freeze, "lib/ollama/message.rb".freeze, "lib/ollama/options.rb".freeze, "lib/ollama/response.rb".freeze, "lib/ollama/tool.rb".freeze, "lib/ollama/tool/function.rb".freeze, "lib/ollama/tool/function/parameters.rb".freeze, "lib/ollama/tool/function/parameters/property.rb".freeze, "lib/ollama/utils/ansi_markdown.rb".freeze, "lib/ollama/utils/cache_fetcher.rb".freeze, "lib/ollama/utils/chooser.rb".freeze, "lib/ollama/utils/colorize_texts.rb".freeze, "lib/ollama/utils/fetcher.rb".freeze, "lib/ollama/utils/file_argument.rb".freeze, "lib/ollama/utils/math.rb".freeze, "lib/ollama/utils/tags.rb".freeze, "lib/ollama/utils/width.rb".freeze, "lib/ollama/version.rb".freeze, "ollama-ruby.gemspec".freeze, "spec/assets/embeddings.json".freeze, "spec/assets/kitten.jpg".freeze, "spec/assets/prompt.txt".freeze, "spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/chat_spec.rb".freeze, "spec/ollama/commands/copy_spec.rb".freeze, "spec/ollama/commands/create_spec.rb".freeze, "spec/ollama/commands/delete_spec.rb".freeze, "spec/ollama/commands/embed_spec.rb".freeze, "spec/ollama/commands/embeddings_spec.rb".freeze, "spec/ollama/commands/generate_spec.rb".freeze, "spec/ollama/commands/ps_spec.rb".freeze, "spec/ollama/commands/pull_spec.rb".freeze, "spec/ollama/commands/push_spec.rb".freeze, "spec/ollama/commands/show_spec.rb".freeze, "spec/ollama/commands/tags_spec.rb".freeze, "spec/ollama/documents/memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_backed_memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_cache_spec.rb".freeze, "spec/ollama/documents/splitters/character_spec.rb".freeze, "spec/ollama/documents/splitters/semantic_spec.rb".freeze, "spec/ollama/documents_spec.rb".freeze, "spec/ollama/handlers/collector_spec.rb".freeze, "spec/ollama/handlers/dump_json_spec.rb".freeze, "spec/ollama/handlers/dump_yaml_spec.rb".freeze, "spec/ollama/handlers/markdown_spec.rb".freeze, "spec/ollama/handlers/nop_spec.rb".freeze, "spec/ollama/handlers/print_spec.rb".freeze, "spec/ollama/handlers/progress_spec.rb".freeze, "spec/ollama/handlers/say_spec.rb".freeze, "spec/ollama/handlers/single_spec.rb".freeze, "spec/ollama/image_spec.rb".freeze, "spec/ollama/message_spec.rb".freeze, "spec/ollama/options_spec.rb".freeze, "spec/ollama/tool_spec.rb".freeze, "spec/ollama/utils/ansi_markdown_spec.rb".freeze, "spec/ollama/utils/cache_fetcher_spec.rb".freeze, "spec/ollama/utils/colorize_texts_spec.rb".freeze, "spec/ollama/utils/fetcher_spec.rb".freeze, "spec/ollama/utils/file_argument_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/ollama/utils/width_spec.rb".freeze, "spec/spec_helper.rb".freeze, "tmp/.keep".freeze]
17
17
  s.homepage = "https://github.com/flori/ollama-ruby".freeze
18
18
  s.licenses = ["MIT".freeze]
19
19
  s.rdoc_options = ["--title".freeze, "Ollama-ruby - Interacting with the Ollama API".freeze, "--main".freeze, "README.md".freeze]
20
20
  s.required_ruby_version = Gem::Requirement.new("~> 3.1".freeze)
21
21
  s.rubygems_version = "3.5.18".freeze
22
22
  s.summary = "Interacting with the Ollama API".freeze
23
- s.test_files = ["spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/chat_spec.rb".freeze, "spec/ollama/commands/copy_spec.rb".freeze, "spec/ollama/commands/create_spec.rb".freeze, "spec/ollama/commands/delete_spec.rb".freeze, "spec/ollama/commands/embed_spec.rb".freeze, "spec/ollama/commands/embeddings_spec.rb".freeze, "spec/ollama/commands/generate_spec.rb".freeze, "spec/ollama/commands/ps_spec.rb".freeze, "spec/ollama/commands/pull_spec.rb".freeze, "spec/ollama/commands/push_spec.rb".freeze, "spec/ollama/commands/show_spec.rb".freeze, "spec/ollama/commands/tags_spec.rb".freeze, "spec/ollama/documents/memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_backed_memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_cache_spec.rb".freeze, "spec/ollama/documents/splitters/character_spec.rb".freeze, "spec/ollama/documents/splitters/semantic_spec.rb".freeze, "spec/ollama/documents_spec.rb".freeze, "spec/ollama/handlers/collector_spec.rb".freeze, "spec/ollama/handlers/dump_json_spec.rb".freeze, "spec/ollama/handlers/dump_yaml_spec.rb".freeze, "spec/ollama/handlers/markdown_spec.rb".freeze, "spec/ollama/handlers/nop_spec.rb".freeze, "spec/ollama/handlers/print_spec.rb".freeze, "spec/ollama/handlers/progress_spec.rb".freeze, "spec/ollama/handlers/say_spec.rb".freeze, "spec/ollama/handlers/single_spec.rb".freeze, "spec/ollama/image_spec.rb".freeze, "spec/ollama/message_spec.rb".freeze, "spec/ollama/options_spec.rb".freeze, "spec/ollama/tool_spec.rb".freeze, "spec/ollama/utils/ansi_markdown_spec.rb".freeze, "spec/ollama/utils/cache_fetcher_spec.rb".freeze, "spec/ollama/utils/fetcher_spec.rb".freeze, "spec/ollama/utils/file_argument_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/ollama/utils/width_spec.rb".freeze, "spec/spec_helper.rb".freeze]
23
+ s.test_files = ["spec/ollama/client/doc_spec.rb".freeze, "spec/ollama/client_spec.rb".freeze, "spec/ollama/commands/chat_spec.rb".freeze, "spec/ollama/commands/copy_spec.rb".freeze, "spec/ollama/commands/create_spec.rb".freeze, "spec/ollama/commands/delete_spec.rb".freeze, "spec/ollama/commands/embed_spec.rb".freeze, "spec/ollama/commands/embeddings_spec.rb".freeze, "spec/ollama/commands/generate_spec.rb".freeze, "spec/ollama/commands/ps_spec.rb".freeze, "spec/ollama/commands/pull_spec.rb".freeze, "spec/ollama/commands/push_spec.rb".freeze, "spec/ollama/commands/show_spec.rb".freeze, "spec/ollama/commands/tags_spec.rb".freeze, "spec/ollama/documents/memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_backed_memory_cache_spec.rb".freeze, "spec/ollama/documents/redis_cache_spec.rb".freeze, "spec/ollama/documents/splitters/character_spec.rb".freeze, "spec/ollama/documents/splitters/semantic_spec.rb".freeze, "spec/ollama/documents_spec.rb".freeze, "spec/ollama/handlers/collector_spec.rb".freeze, "spec/ollama/handlers/dump_json_spec.rb".freeze, "spec/ollama/handlers/dump_yaml_spec.rb".freeze, "spec/ollama/handlers/markdown_spec.rb".freeze, "spec/ollama/handlers/nop_spec.rb".freeze, "spec/ollama/handlers/print_spec.rb".freeze, "spec/ollama/handlers/progress_spec.rb".freeze, "spec/ollama/handlers/say_spec.rb".freeze, "spec/ollama/handlers/single_spec.rb".freeze, "spec/ollama/image_spec.rb".freeze, "spec/ollama/message_spec.rb".freeze, "spec/ollama/options_spec.rb".freeze, "spec/ollama/tool_spec.rb".freeze, "spec/ollama/utils/ansi_markdown_spec.rb".freeze, "spec/ollama/utils/cache_fetcher_spec.rb".freeze, "spec/ollama/utils/colorize_texts_spec.rb".freeze, "spec/ollama/utils/fetcher_spec.rb".freeze, "spec/ollama/utils/file_argument_spec.rb".freeze, "spec/ollama/utils/tags_spec.rb".freeze, "spec/ollama/utils/width_spec.rb".freeze, "spec/spec_helper.rb".freeze]
24
24
 
25
25
  s.specification_version = 4
26
26
 
@@ -60,16 +60,16 @@ RSpec.describe Ollama::Documents do
60
60
  expect(ollama).to receive(:embed).
61
61
  with(model:, input: [ 'foo' ], options: nil).
62
62
  and_return(double(embeddings: [ [ 0.1 ] ]))
63
- expect(documents.add('foo', tags: %i[ test ])).to eq documents
63
+ expect(documents.add('foo', tags: %w[ test ])).to eq documents
64
64
  expect(ollama).to receive(:embed).
65
65
  with(model:, input: 'foo', options: nil).
66
66
  and_return(double(embeddings: [ [ 0.1 ] ]))
67
- records = documents.find('foo', tags: %i[ nix ])
67
+ records = documents.find('foo', tags: %w[ nix ])
68
68
  expect(records).to eq []
69
69
  expect(ollama).to receive(:embed).
70
70
  with(model:, input: 'foo', options: nil).
71
71
  and_return(double(embeddings: [ [ 0.1 ] ]))
72
- records = documents.find('foo', tags: %i[ test ])
72
+ records = documents.find('foo', tags: %w[ test ])
73
73
  expect(records).to eq [
74
74
  Ollama::Documents::Record[text: 'foo', embedding: [ 0.1 ], similarity: 1.0 ]
75
75
  ]
@@ -135,8 +135,9 @@ RSpec.describe Ollama::Documents do
135
135
  expect(ollama).to receive(:embed).
136
136
  with(model:, input: %w[ bar ], options: nil).
137
137
  and_return(double(embeddings: [ [ 0.1 ] ]))
138
- expect(documents.add('foo', tags: %i[ test ])).to eq documents
139
- expect(documents.add('bar', tags: %i[ test2 ])).to eq documents
138
+ expect(documents.add('foo', tags: %w[ test ])).to eq documents
139
+ expect(documents.add('bar', tags: %w[ test2 ])).to eq documents
140
+ expect(documents.tags.to_a).to eq %w[ test test2 ]
140
141
  expect {
141
142
  documents.clear tags: 'test'
142
143
  }.to change { documents.size }.from(2).to(1)
@@ -13,6 +13,10 @@ RSpec.describe Ollama::Options do
13
13
  expect(options).to be_a described_class
14
14
  end
15
15
 
16
+ it 'can be empty' do
17
+ expect(described_class.new).to be_empty
18
+ end
19
+
16
20
  it 'can be used to cast hashes' do
17
21
  expect(described_class[{
18
22
  penalize_newline: true,
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Utils::ColorizeTexts do
4
+ it 'colorizes texts' do
5
+ ct = described_class.new(%w[ foo bar ])
6
+ colored = ct.to_s
7
+ uncolored = Term::ANSIColor.uncolor(ct.to_s)
8
+ expect(colored.size).to be > uncolored.size
9
+ expect(uncolored).to eq(
10
+ "foo\n#3 \n\nbar\n#3 \n\n"
11
+ )
12
+ end
13
+ end
@@ -19,7 +19,7 @@ RSpec.describe Ollama::Utils::Fetcher do
19
19
  end
20
20
 
21
21
  it 'can #get with streaming' do
22
- stub_request(:get, 'https://www.example.com/hello').
22
+ stub_request(:get, url).
23
23
  with(headers: fetcher.headers).
24
24
  to_return(
25
25
  status: 200,
@@ -37,7 +37,7 @@ RSpec.describe Ollama::Utils::Fetcher do
37
37
  fetcher = described_class.new(
38
38
  http_options: { ssl_verify_peer: false }
39
39
  ).expose
40
- stub_request(:get, 'https://www.example.com/hello').
40
+ stub_request(:get, url).
41
41
  with(headers: fetcher.headers).
42
42
  to_return(
43
43
  status: 200,
@@ -45,7 +45,7 @@ RSpec.describe Ollama::Utils::Fetcher do
45
45
  headers: { 'Content-Type' => 'text/plain' },
46
46
  )
47
47
  expect(Excon).to receive(:new).with(
48
- 'https://www.example.com/hello',
48
+ url,
49
49
  hash_including(ssl_verify_peer: false)
50
50
  ).and_call_original
51
51
  fetcher.get(url) do |tmp|
@@ -56,7 +56,7 @@ RSpec.describe Ollama::Utils::Fetcher do
56
56
  end
57
57
 
58
58
  it 'can #get and fallback from streaming' do
59
- stub_request(:get, 'https://www.example.com/hello').
59
+ stub_request(:get, url).
60
60
  with(headers: fetcher.headers).
61
61
  to_return(
62
62
  { status: 501 },
@@ -74,9 +74,10 @@ RSpec.describe Ollama::Utils::Fetcher do
74
74
  end
75
75
 
76
76
  it 'can #get and finally fail' do
77
- stub_request(:get, 'https://www.example.com/hello').
77
+ stub_request(:get, url).
78
78
  with(headers: fetcher.headers).
79
79
  to_return(status: 500)
80
+ expect(STDERR).to receive(:puts).with(/cannot.*get.*#{url}/i)
80
81
  fetcher.get(url) do |tmp|
81
82
  expect(tmp).to be_a StringIO
82
83
  expect(tmp.read).to eq ''
@@ -106,10 +107,31 @@ RSpec.describe Ollama::Utils::Fetcher do
106
107
 
107
108
  it 'can .execute and fail' do
108
109
  expect(IO).to receive(:popen).and_raise StandardError
110
+ expect(STDERR).to receive(:puts).with(/cannot.*execute.*foobar/i)
109
111
  described_class.execute('foobar') do |file|
110
112
  expect(file).to be_a StringIO
111
113
  expect(file.read).to be_empty
112
114
  expect(file.content_type).to eq 'text/plain'
113
115
  end
114
116
  end
117
+
118
+ describe '.normalize_url' do
119
+ it 'can handle umlauts' do
120
+ expect(described_class.normalize_url('https://foo.de/bär')).to eq(
121
+ 'https://foo.de/b%C3%A4r'
122
+ )
123
+ end
124
+
125
+ it 'can handle escaped umlauts' do
126
+ expect(described_class.normalize_url('https://foo.de/b%C3%A4r')).to eq(
127
+ 'https://foo.de/b%C3%A4r'
128
+ )
129
+ end
130
+
131
+ it 'can remove #anchors' do
132
+ expect(described_class.normalize_url('https://foo.de#bar')).to eq(
133
+ 'https://foo.de'
134
+ )
135
+ end
136
+ end
115
137
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ollama-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-18 00:00:00.000000000 Z
11
+ date: 2024-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gem_hadar
@@ -516,6 +516,7 @@ files:
516
516
  - spec/ollama/tool_spec.rb
517
517
  - spec/ollama/utils/ansi_markdown_spec.rb
518
518
  - spec/ollama/utils/cache_fetcher_spec.rb
519
+ - spec/ollama/utils/colorize_texts_spec.rb
519
520
  - spec/ollama/utils/fetcher_spec.rb
520
521
  - spec/ollama/utils/file_argument_spec.rb
521
522
  - spec/ollama/utils/tags_spec.rb
@@ -585,6 +586,7 @@ test_files:
585
586
  - spec/ollama/tool_spec.rb
586
587
  - spec/ollama/utils/ansi_markdown_spec.rb
587
588
  - spec/ollama/utils/cache_fetcher_spec.rb
589
+ - spec/ollama/utils/colorize_texts_spec.rb
588
590
  - spec/ollama/utils/fetcher_spec.rb
589
591
  - spec/ollama/utils/file_argument_spec.rb
590
592
  - spec/ollama/utils/tags_spec.rb