ollama-ruby 0.3.1 → 0.4.0

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: 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
  [