ollama-ruby 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0e3428486bf4d60d1e4a64574a9c04bb41da5a5fe76b5f4bc37c5f089221cf3
4
- data.tar.gz: 0a2f6f5269c97b22493400448204116aebef642273c024aee474a38741dfdce5
3
+ metadata.gz: 29863f68627a05541de5eab810850b0509db94c1d91b2e9a5a9be9bd387e8aea
4
+ data.tar.gz: 07f3558153a0a724c08e27a86482e0951070348bf25641d2a9feed113e3e7046
5
5
  SHA512:
6
- metadata.gz: 3fd8db76a1cad30ba41af287e1abb77a102913f09f1648c9fbaf89f5e3bf4f3dace4a39cc6b72da456b633eb42d331b356134aecc196b112c7f94215177b4023
7
- data.tar.gz: 4541a1e93c2714b786cb21b5e401ffc48a08e05c39bfac860e8cdd275a447bbc7db0b6cf80fee5f459f86734320a90362418a50f4174d53ece4e1a9d2f1dc647
6
+ metadata.gz: 11c405277b59c6acc75c24220942dd883bb7aec99d97d19bc9baf8d61aa4e5dc59af905631dc37641b91babdd9e5ab7305720a8720451db7b4bb6ca067833913
7
+ data.tar.gz: 129f49e2d4b5cb65b7999f666f33798511b8a34b1be84bbc7bd4433872cd7c323812cd4dd3e8d2ae325cb013cfb6403db8181c8cbe641139469e5ea9821cc71d
data/CHANGES.md CHANGED
@@ -1,5 +1,53 @@
1
1
  # Changes
2
2
 
3
+ ## 2024-09-21 v0.4.0
4
+
5
+ ### Change Log for **1.2.3**
6
+
7
+ #### New Features
8
+
9
+ * Added `-E` option to disable embeddings for this chat session.
10
+ * Added `-M` option to load document embeddings into (empty) MemoryCache for this chat session.
11
+ * Added CSV parsing support to `ollama_chat`.
12
+ * Improved error handling in `Ollama::Utils::Fetcher` methods.
13
+
14
+ #### Bug Fixes
15
+
16
+ * Handle case in `ollama_chat` where responses don't provide counts, display 0
17
+ rates instead.
18
+
19
+ #### Refactoring and Improvements
20
+
21
+ * Updated eval count and rate display in FollowChat class.
22
+ * Refactor system prompt display and chunk listing in chat output.
23
+ * Refactor cache implementation to use Ollama::Documents::Cache::Common module.
24
+ * Improved system prompt formatting in `ollama_chat` script.
25
+ * Renamed `tags` method to `tags_set` in `Ollama::Documents::Record` class.
26
+
27
+ #### Documentation
28
+
29
+ * Added comments to ColorizeTexts utility class.
30
+
31
+ ## 2024-09-15 v0.3.2
32
+
33
+ * Add color support to chooser module:
34
+ + Include `Term::ANSIColor` in `Ollama::Utils::Chooser` module
35
+ + Use `blue`, `on_blue` ANSI color for selected item in query method
36
+ * Refactor summarize method to also import sources:
37
+ + Added `content` variable to store result of `parse_source`
38
+ + Replaced `or return` with explicit assignment and return
39
+ + Added calls to `source_io.rewind` and `import_document`
40
+ * Add new test for `file_argument_spec.rb`
41
+ * Refactor tag list initialization and merging:
42
+ + Use array literals for initializing tags lists
43
+ + Use array literals for passing to merge method
44
+ * Update dependencies and dates in Rakefile and gemspec:
45
+ + Removed '.utilsrc' from ignored files in Rakefile
46
+ + Updated date in `ollama-ruby.gemspec` to "2024-09-13"
47
+ + Removed 'utils' development dependency from `ollama-ruby.gemspec`
48
+ * Refactor `search_web` method to allow n parameter to be optional and default
49
+ to 1.
50
+
3
51
  ## 2024-09-12 v0.3.1
4
52
 
5
53
  * Update dependencies and date in gemspec files:
data/README.md CHANGED
@@ -43,6 +43,8 @@ ollama_chat [OPTIONS]
43
43
  -c CHAT a saved chat conversation to load
44
44
  -C COLLECTION name of the collection used in this conversation
45
45
  -D DOCUMENT load document and add to collection (multiple)
46
+ -M use (empty) MemoryCache for this chat session
47
+ -E disable embeddings for this chat session
46
48
  -v use voice output
47
49
  -h this help
48
50
  ```
data/Rakefile CHANGED
@@ -13,9 +13,10 @@ GemHadar do
13
13
  description 'Library that allows interacting with the Ollama API'
14
14
  test_dir 'spec'
15
15
  ignore '.*.sw[pon]', 'pkg', 'Gemfile.lock', '.AppleDouble', '.bundle',
16
- '.yardoc', 'tags', 'errors.lst', 'cscope.out', 'coverage', 'tmp', 'corpus'
16
+ '.yardoc', 'doc', 'tags', 'errors.lst', 'cscope.out', 'coverage', 'tmp',
17
+ 'corpus'
17
18
  package_ignore '.all_images.yml', '.tool-versions', '.gitignore', 'VERSION',
18
- '.utilsrc', '.rspec', *Dir.glob('.github/**/*', File::FNM_DOTMATCH)
19
+ '.rspec', *Dir.glob('.github/**/*', File::FNM_DOTMATCH)
19
20
  readme 'README.md'
20
21
 
21
22
  executables << 'ollama_console' << 'ollama_chat' <<
@@ -40,8 +41,9 @@ GemHadar do
40
41
  dependency 'pdf-reader', '~> 2.0'
41
42
  development_dependency 'all_images', '~> 0.4'
42
43
  development_dependency 'rspec', '~> 3.2'
43
- development_dependency 'utils'
44
44
  development_dependency 'webmock'
45
+ development_dependency 'debug'
46
+ development_dependency 'simplecov'
45
47
 
46
48
  licenses << 'MIT'
47
49
 
data/bin/ollama_chat CHANGED
@@ -14,6 +14,7 @@ require 'uri'
14
14
  require 'nokogiri'
15
15
  require 'rss'
16
16
  require 'pdf/reader'
17
+ require 'csv'
17
18
 
18
19
  class OllamaChatConfig
19
20
  include ComplexConfig
@@ -47,7 +48,7 @@ class OllamaChatConfig
47
48
  splitter:
48
49
  name: RecursiveCharacter
49
50
  chunk_size: 1024
50
- cache: Ollama::Documents::RedisCache
51
+ cache: Ollama::Documents::Cache::RedisBackedMemoryCache
51
52
  redis:
52
53
  url: <%= ENV.fetch('REDIS_URL', 'null') %>
53
54
  debug: <%= ENV['OLLAMA_CHAT_DEBUG'].to_i == 1 ? true : false %>
@@ -130,11 +131,11 @@ class FollowChat
130
131
  prompt_eval_duration = response.prompt_eval_duration / 1e9
131
132
  stats_text = {
132
133
  eval_duration: Tins::Duration.new(eval_duration),
133
- eval_count: response.eval_count,
134
- eval_rate: bold { "%.2f c/s" % (response.eval_count / eval_duration) } + color(111),
134
+ eval_count: response.eval_count.to_i,
135
+ eval_rate: bold { "%.2f c/s" % (response.eval_count.to_i / eval_duration) } + color(111),
135
136
  prompt_eval_duration: Tins::Duration.new(prompt_eval_duration),
136
- prompt_eval_count: response.prompt_eval_count,
137
- prompt_eval_rate: bold { "%.2f c/s" % (response.prompt_eval_count / prompt_eval_duration) } + color(111),
137
+ prompt_eval_count: response.prompt_eval_count.to_i,
138
+ prompt_eval_rate: bold { "%.2f c/s" % (response.prompt_eval_count.to_i / prompt_eval_duration) } + color(111),
138
139
  total_duration: Tins::Duration.new(response.total_duration / 1e9),
139
140
  load_duration: Tins::Duration.new(response.load_duration / 1e9),
140
141
  }.map { _1 * '=' } * ' '
@@ -144,10 +145,12 @@ class FollowChat
144
145
  end
145
146
  end
146
147
 
147
- def search_web(query, n = 5)
148
+ def search_web(query, n = nil)
149
+ n = n.to_i
150
+ n < 1 and n = 1
148
151
  query = URI.encode_uri_component(query)
149
152
  url = "https://www.duckduckgo.com/html/?q=#{query}"
150
- Ollama::Utils::Fetcher.new.get(url) do |tmp|
153
+ Ollama::Utils::Fetcher.new(debug: $config.debug).get(url) do |tmp|
151
154
  result = []
152
155
  doc = Nokogiri::HTML(tmp)
153
156
  doc.css('.results_links').each do |link|
@@ -300,6 +303,16 @@ def parse_source(source_io)
300
303
  end
301
304
  source_io.rewind
302
305
  source_io.read
306
+ when 'text/csv'
307
+ result = +''
308
+ CSV.table(File.new(source_io), col_sep: ?,).each do |row|
309
+ next if row.fields.select(&:present?).size == 0
310
+ result << row.map { |pair|
311
+ pair.compact.map { _1.to_s.strip } * ': ' if pair.last.present?
312
+ }.select(&:present?).map { _1.prepend(' ') } * ?\n
313
+ result << "\n\n"
314
+ end
315
+ result
303
316
  when %r(\Atext/)
304
317
  source_io.read
305
318
  when 'application/rss+xml'
@@ -310,11 +323,7 @@ def parse_source(source_io)
310
323
  source_io.read
311
324
  when 'application/pdf'
312
325
  reader = PDF::Reader.new(source_io)
313
- result = +''
314
- reader.pages.each do |page|
315
- result << page.text
316
- end
317
- result
326
+ reader.pages.inject(+'') { |result, page| result << page.text }
318
327
  else
319
328
  STDERR.puts "Cannot import #{source_io&.content_type} document."
320
329
  return
@@ -322,10 +331,7 @@ def parse_source(source_io)
322
331
  end
323
332
 
324
333
  def import_document(source_io, source)
325
- unless $config.embedding.enabled
326
- STDOUT.puts "Embedding disabled, I won't import any documents, try: /summarize"
327
- return
328
- end
334
+ embedding_enabled? or return parse_source(source_io)
329
335
  puts "Importing #{italic { source_io&.content_type }} document #{source.to_s.inspect}."
330
336
  text = parse_source(source_io) or return
331
337
  text.downcase!
@@ -362,7 +368,7 @@ end
362
368
  def fetch_source(source, &block)
363
369
  case source
364
370
  when %r(\Ahttps?://\S+)
365
- Utils::Fetcher.get(source) do |tmp|
371
+ Utils::Fetcher.get(source, debug: $config.debug) do |tmp|
366
372
  block.(tmp)
367
373
  end
368
374
  when %r(\Afile://(?:(?:[.-]|[[:alnum:]])*)(/\S*)|([~.]?/\S*))
@@ -382,7 +388,11 @@ def summarize(source)
382
388
  puts "Now summarizing #{source.to_s.inspect}."
383
389
  source_content =
384
390
  fetch_source(source) do |source_io|
385
- parse_source(source_io) or return
391
+ content = parse_source(source_io)
392
+ content.present? or return
393
+ source_io.rewind
394
+ import_document(source_io, source)
395
+ content
386
396
  end
387
397
  $config.prompts.summarize % source_content
388
398
  end
@@ -428,7 +438,7 @@ ensure
428
438
  end
429
439
 
430
440
  def choose_collection(default_collection)
431
- collections = [ default_collection ] + $documents.collections
441
+ collections = [ default_collection ] + $documents.collections.map(&:to_s)
432
442
  collections = collections.uniq.sort
433
443
  $documents.collection = collection =
434
444
  Ollama::Utils::Chooser.choose(collections) || default_collection
@@ -447,7 +457,11 @@ def collection_stats
447
457
  end
448
458
 
449
459
  def configure_cache
450
- Object.const_get($config.cache)
460
+ if $opts[?M]
461
+ Ollama::Documents::MemoryCache
462
+ else
463
+ Object.const_get($config.cache)
464
+ end
451
465
  rescue => e
452
466
  STDERR.puts "Caught #{e.class}: #{e} => Falling back to MemoryCache."
453
467
  Ollama::Documents::MemoryCache
@@ -463,6 +477,14 @@ def set_markdown(value)
463
477
  end
464
478
  end
465
479
 
480
+ def clear_messages(messages)
481
+ messages.delete_if { _1.role != 'system' }
482
+ end
483
+
484
+ def embedding_enabled?
485
+ $config.embedding.enabled && !$opts[?E]
486
+ end
487
+
466
488
  def display_chat_help
467
489
  puts <<~end
468
490
  /paste to paste content
@@ -493,7 +515,9 @@ def usage
493
515
  -s SYSTEM the system prompt to use as a file, OLLAMA_CHAT_SYSTEM
494
516
  -c CHAT a saved chat conversation to load
495
517
  -C COLLECTION name of the collection used in this conversation
496
- -D DOCUMENT load document and add to collection (multiple)
518
+ -D DOCUMENT load document and add to embeddings collection (multiple)
519
+ -M use (empty) MemoryCache for this chat session
520
+ -E disable embeddings for this chat session
497
521
  -v use voice output
498
522
  -h this help
499
523
 
@@ -505,28 +529,28 @@ def ollama
505
529
  $ollama
506
530
  end
507
531
 
508
- opts = go 'f:u:m:s:c:C:D:vh'
532
+ $opts = go 'f:u:m:s:c:C:D:MEvh'
509
533
 
510
- config = OllamaChatConfig.new(opts[?f])
534
+ config = OllamaChatConfig.new($opts[?f])
511
535
  $config = config.config
512
536
 
513
- opts[?h] and usage
537
+ $opts[?h] and usage
514
538
 
515
539
  puts "Configuration read from #{config.filename.inspect} is:", $config
516
540
 
517
- base_url = opts[?u] || $config.url
541
+ base_url = $opts[?u] || $config.url
518
542
  $ollama = Client.new(base_url:, debug: $config.debug)
519
543
 
520
- model = choose_model(opts[?m], $config.model.name)
544
+ model = choose_model($opts[?m], $config.model.name)
521
545
  options = Options[$config.model.options]
522
546
  model_system = pull_model_unless_present(model, options)
523
547
  messages = []
524
548
 
525
- if $config.embedding.enabled
549
+ if embedding_enabled?
526
550
  embedding_model = $config.embedding.model.name
527
551
  embedding_model_options = Options[$config.embedding.model.options]
528
552
  pull_model_unless_present(embedding_model, embedding_model_options)
529
- collection = opts[?C] || $config.embedding.collection
553
+ collection = $opts[?C] || $config.embedding.collection
530
554
  $documents = Documents.new(
531
555
  ollama:,
532
556
  model: $config.embedding.model.name,
@@ -536,7 +560,7 @@ if $config.embedding.enabled
536
560
  redis_url: $config.redis.url?,
537
561
  )
538
562
 
539
- document_list = opts[?D].to_a
563
+ document_list = $opts[?D].to_a
540
564
  if document_list.any?(&:empty?)
541
565
  puts "Clearing collection #{bold{collection}}."
542
566
  $documents.clear
@@ -564,18 +588,21 @@ else
564
588
  $documents = Documents.new(ollama:, model:)
565
589
  end
566
590
 
567
- if voice = ($config.voice if opts[?v])
591
+ if voice = ($config.voice if $opts[?v])
568
592
  puts "Using voice #{bold{voice}} to speak."
569
593
  end
570
594
  markdown = set_markdown($config.markdown)
571
595
 
572
- if opts[?c]
573
- messages.concat load_conversation(opts[?c])
596
+ if $opts[?c]
597
+ messages.concat load_conversation($opts[?c])
574
598
  else
575
599
  if system = Ollama::Utils::FileArgument.
576
- get_file_argument(opts[?s], default: $config.prompts.system? || model_system)
600
+ get_file_argument($opts[?s], default: $config.prompts.system? || model_system)
577
601
  messages << Message.new(role: 'system', content: system)
578
- puts "Configured system prompt is:\n#{italic { system }}"
602
+ puts <<~end
603
+ Configured system prompt is:
604
+ #{italic{Ollama::Utils::Width.wrap(system, percentage: 90)}}
605
+ end
579
606
  end
580
607
  end
581
608
 
@@ -601,11 +628,11 @@ loop do
601
628
  list_conversation(messages, markdown)
602
629
  next
603
630
  when %r(^/clear$)
604
- messages.clear
631
+ clear_messages(messages)
605
632
  puts "Cleared messages."
606
633
  next
607
634
  when %r(^/clobber$)
608
- messages.clear
635
+ clear_messages(messages)
609
636
  $documents.clear
610
637
  puts "Cleared messages and collection."
611
638
  next
@@ -648,6 +675,8 @@ loop do
648
675
  puts "Not enough messages in this conversation."
649
676
  redo
650
677
  end
678
+ parse_content = false
679
+ content
651
680
  when %r(^/summarize\s+(.+))
652
681
  parse_content = false
653
682
  content = summarize($1) or next
@@ -676,9 +705,12 @@ loop do
676
705
  when %r(^/)
677
706
  display_chat_help
678
707
  next
679
- when nil, ''
708
+ when ''
680
709
  puts "Type /quit to quit."
681
710
  next
711
+ when nil
712
+ puts "Goodbye."
713
+ exit 0
682
714
  end
683
715
 
684
716
  content, tags = if parse_content
@@ -687,7 +719,7 @@ loop do
687
719
  [ content, Utils::Tags.new ]
688
720
  end
689
721
 
690
- if $config.embedding.enabled && content
722
+ if embedding_enabled? && content
691
723
  records = $documents.find_where(
692
724
  content.downcase,
693
725
  tags:,
@@ -695,10 +727,9 @@ loop do
695
727
  text_size: $config.embedding.found_texts_size?,
696
728
  text_count: $config.embedding.found_texts_count?,
697
729
  )
698
- found_texts = records.map(&:text)
699
- unless found_texts.empty?
700
- content += "\nConsider these chunks for your answer:\n"\
701
- "#{found_texts.join("\n\n---\n\n")}"
730
+ unless records.empty?
731
+ content += "\nConsider these chunks for your answer:\n\n"\
732
+ "#{records.map { [ _1.text, _1.tags_set ] * ?\n }.join("\n\n---\n\n")}"
702
733
  end
703
734
  end
704
735
 
@@ -706,8 +737,8 @@ loop do
706
737
  handler = FollowChat.new(messages:, markdown:, voice:)
707
738
  ollama.chat(model:, messages:, options:, stream: true, &handler)
708
739
 
709
- if records
710
- puts records.map { |record|
740
+ if embedding_enabled? && !records.empty?
741
+ puts "", records.map { |record|
711
742
  link = if record.source =~ %r(\Ahttps?://)
712
743
  record.source
713
744
  else
@@ -0,0 +1,17 @@
1
+ module Ollama::Documents::Cache::Common
2
+ attr_writer :prefix
3
+
4
+ def collections(prefix)
5
+ unique = Set.new
6
+ full_each { |key, _| unique << key[/\A#{prefix}(.*)-/, 1] }
7
+ unique.map(&:to_sym)
8
+ end
9
+
10
+ def pre(key)
11
+ [ @prefix, key ].join
12
+ end
13
+
14
+ def unpre(key)
15
+ key.sub(/\A#@prefix/, '')
16
+ end
17
+ end
@@ -1,11 +1,13 @@
1
+ require 'ollama/documents/cache/common'
2
+
1
3
  class Ollama::Documents::MemoryCache
4
+ include Ollama::Documents::Cache::Common
5
+
2
6
  def initialize(prefix:)
3
7
  @prefix = prefix
4
8
  @data = {}
5
9
  end
6
10
 
7
- attr_writer :prefix
8
-
9
11
  def [](key)
10
12
  @data[pre(key)]
11
13
  end
@@ -23,11 +25,11 @@ class Ollama::Documents::MemoryCache
23
25
  end
24
26
 
25
27
  def size
26
- @data.size
28
+ count
27
29
  end
28
30
 
29
31
  def clear
30
- @data.clear
32
+ @data.delete_if { |key, _| key.start_with?(@prefix) }
31
33
  self
32
34
  end
33
35
 
@@ -36,11 +38,7 @@ class Ollama::Documents::MemoryCache
36
38
  end
37
39
  include Enumerable
38
40
 
39
- def pre(key)
40
- [ @prefix, key ].join
41
- end
42
-
43
- def unpre(key)
44
- key.sub(/\A#@prefix/, '')
41
+ def full_each(&block)
42
+ @data.each(&block)
45
43
  end
46
44
  end
@@ -0,0 +1,44 @@
1
+ require 'redis'
2
+
3
+ class Ollama::Documents
4
+ class RedisBackedMemoryCache < MemoryCache
5
+ def initialize(prefix:, url: ENV['REDIS_URL'])
6
+ super(prefix:)
7
+ url or raise ArgumentError, 'require redis url'
8
+ @prefix, @url = prefix, url
9
+ @redis_cache = Ollama::Documents::RedisCache.new(prefix:, url:)
10
+ @redis_cache.full_each do |key, value|
11
+ @data[key] = value
12
+ end
13
+ end
14
+
15
+ def redis
16
+ @redis_cache.redis
17
+ end
18
+
19
+ def []=(key, value)
20
+ super
21
+ redis.set(pre(key), JSON(value))
22
+ end
23
+
24
+ def delete(key)
25
+ result = redis.del(pre(key))
26
+ super
27
+ result
28
+ end
29
+
30
+ def clear
31
+ redis.scan_each(match: "#@prefix*") { |key| redis.del(key) }
32
+ super
33
+ self
34
+ end
35
+
36
+ def pre(key)
37
+ [ @prefix, key ].join
38
+ end
39
+
40
+ def unpre(key)
41
+ key.sub(/\A#@prefix/, '')
42
+ end
43
+ end
44
+ end
@@ -1,13 +1,14 @@
1
+ require 'ollama/documents/cache/common'
1
2
  require 'redis'
2
3
 
3
4
  class Ollama::Documents::RedisCache
5
+ include Ollama::Documents::Cache::Common
6
+
4
7
  def initialize(prefix:, url: ENV['REDIS_URL'])
5
8
  url or raise ArgumentError, 'require redis url'
6
9
  @prefix, @url = prefix, url
7
10
  end
8
11
 
9
- attr_writer :prefix
10
-
11
12
  def redis
12
13
  @redis ||= Redis.new(url: @url)
13
14
  end
@@ -48,11 +49,11 @@ class Ollama::Documents::RedisCache
48
49
  end
49
50
  include Enumerable
50
51
 
51
- def pre(key)
52
- [ @prefix, key ].join
53
- end
54
-
55
- def unpre(key)
56
- key.sub(/\A#@prefix/, '')
52
+ def full_each(&block)
53
+ redis.scan_each do |key|
54
+ value = redis.get(key) or next
55
+ value = JSON(value, object_class: Ollama::Documents::Record)
56
+ block.(key, value)
57
+ end
57
58
  end
58
59
  end
@@ -3,8 +3,11 @@ require 'digest'
3
3
 
4
4
  class Ollama::Documents
5
5
  end
6
- require 'ollama/documents/memory_cache'
7
- require 'ollama/documents/redis_cache'
6
+ class Ollama::Documents::Cache
7
+ end
8
+ require 'ollama/documents/cache/memory_cache'
9
+ require 'ollama/documents/cache/redis_cache'
10
+ require 'ollama/documents/cache/redis_backed_memory_cache'
8
11
  module Ollama::Documents::Splitters
9
12
  end
10
13
  require 'ollama/documents/splitters/character'
@@ -16,11 +19,15 @@ class Ollama::Documents
16
19
 
17
20
  class Record < JSON::GenericObject
18
21
  def to_s
19
- my_tags = Ollama::Utils::Tags.new(tags)
22
+ my_tags = tags_set
20
23
  my_tags.empty? or my_tags = " #{my_tags}"
21
24
  "#<#{self.class} #{text.inspect}#{my_tags} #{similarity || 'n/a'}>"
22
25
  end
23
26
 
27
+ def tags_set
28
+ Ollama::Utils::Tags.new(tags)
29
+ end
30
+
24
31
  def ==(other)
25
32
  text == other.text
26
33
  end
@@ -28,15 +35,19 @@ class Ollama::Documents
28
35
  alias inspect to_s
29
36
  end
30
37
 
31
- def initialize(ollama:, model:, model_options: nil, collection: :default, cache: MemoryCache, redis_url: nil)
32
- @ollama, @model, @model_options, @collection = ollama, model, model_options, collection
38
+ def initialize(ollama:, model:, model_options: nil, collection: default_collection, cache: MemoryCache, redis_url: nil)
39
+ @ollama, @model, @model_options, @collection = ollama, model, model_options, collection.to_sym
33
40
  @cache, @redis_url = connect_cache(cache), redis_url
34
41
  end
35
42
 
43
+ def default_collection
44
+ :default
45
+ end
46
+
36
47
  attr_reader :ollama, :model, :collection
37
48
 
38
49
  def collection=(new_collection)
39
- @collection = new_collection
50
+ @collection = new_collection.to_sym
40
51
  @cache.prefix = prefix
41
52
  end
42
53
 
@@ -137,15 +148,7 @@ class Ollama::Documents
137
148
  end
138
149
 
139
150
  def collections
140
- case @cache
141
- when MemoryCache
142
- [ @collection ]
143
- when RedisCache
144
- prefix = '%s-' % self.class
145
- Documents::RedisCache.new(prefix:, url: @redis_url).map { _1[/#{prefix}(.*)-/, 1] }.uniq
146
- else
147
- []
148
- end
151
+ ([ default_collection ] + @cache.collections('%s-' % self.class)).uniq
149
152
  end
150
153
 
151
154
  def tags
@@ -156,7 +159,7 @@ class Ollama::Documents
156
159
 
157
160
  def connect_cache(cache_class)
158
161
  cache = nil
159
- if cache_class == RedisCache
162
+ if cache_class.instance_method(:redis)
160
163
  begin
161
164
  cache = cache_class.new(prefix:)
162
165
  cache.size
@@ -1,8 +1,10 @@
1
1
  require 'amatch'
2
2
  require 'search_ui'
3
+ require 'term/ansicolor'
3
4
 
4
5
  module Ollama::Utils::Chooser
5
6
  include SearchUI
7
+ include Term::ANSIColor
6
8
 
7
9
  module_function
8
10
 
@@ -17,7 +19,7 @@ module Ollama::Utils::Chooser
17
19
  },
18
20
  query: -> _answer, matches, selector {
19
21
  matches.each_with_index.map { |m, i|
20
- i == selector ? "#{Term::ANSIColor.blue{?⮕}} #{m.on_blue}" : " #{m}"
22
+ i == selector ? "#{blue{?⮕}} #{on_blue{m}}" : " #{m}"
21
23
  } * ?\n
22
24
  },
23
25
  found: -> _answer, matches, selector {
@@ -3,11 +3,20 @@ class Ollama::Utils::ColorizeTexts
3
3
  include Term::ANSIColor
4
4
  include Ollama::Utils::Width
5
5
 
6
+ # Initializes a new instance of Ollama::Utils::ColorizeTexts
7
+ #
8
+ # @param [Array<String>] texts the array of strings to be displayed with colors
9
+ #
10
+ # @return [Ollama::Utils::ColorizeTexts] an instance of Ollama::Utils::ColorizeTexts
6
11
  def initialize(*texts)
7
- texts = texts.map(&:to_a)
12
+ texts = texts.map(&:to_a)
8
13
  @texts = Array(texts.flatten)
9
14
  end
10
15
 
16
+ # Returns a string representation of the object, including all texts content,
17
+ # colored differently and their sizes.
18
+ #
19
+ # @return [String] The formatted string.
11
20
  def to_s
12
21
  result = +''
13
22
  @texts.each_with_index do |t, i|
@@ -22,6 +31,14 @@ class Ollama::Utils::ColorizeTexts
22
31
 
23
32
  private
24
33
 
34
+ # Returns the nearest RGB color to the given ANSI color
35
+ #
36
+ # @param [color] color The ANSI color attribute
37
+ #
38
+ # @return [Array<RGBTriple>] An array containing two RGB colors, one for black and
39
+ # one for white text, where the first is the closest match to the input color
40
+ # when printed on a black background, and the second is the closest match
41
+ # when printed on a white background.
25
42
  def text_color(color)
26
43
  color = Term::ANSIColor::Attribute[color]
27
44
  [
@@ -30,6 +47,9 @@ class Ollama::Utils::ColorizeTexts
30
47
  ].max_by { |t| t.distance_to(color) }
31
48
  end
32
49
 
50
+ # Returns an array of colors for each step in the gradient
51
+ #
52
+ # @return [Array<Array<Integer>>] An array of RGB color arrays
33
53
  def colors
34
54
  @colors ||= (0..255).map { |i|
35
55
  [