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 +4 -4
- data/CHANGES.md +48 -0
- data/README.md +2 -0
- data/Rakefile +5 -3
- data/bin/ollama_chat +75 -44
- data/lib/ollama/documents/cache/common.rb +17 -0
- data/lib/ollama/documents/{memory_cache.rb → cache/memory_cache.rb} +8 -10
- data/lib/ollama/documents/cache/redis_backed_memory_cache.rb +44 -0
- data/lib/ollama/documents/{redis_cache.rb → cache/redis_cache.rb} +9 -8
- data/lib/ollama/documents.rb +19 -16
- data/lib/ollama/utils/chooser.rb +3 -1
- data/lib/ollama/utils/colorize_texts.rb +21 -1
- data/lib/ollama/utils/fetcher.rb +8 -6
- data/lib/ollama/utils/file_argument.rb +25 -7
- data/lib/ollama/utils/tags.rb +1 -0
- data/lib/ollama/version.rb +1 -1
- data/ollama-ruby.gemspec +8 -7
- data/spec/assets/prompt.txt +1 -0
- data/spec/ollama/documents/memory_cache_spec.rb +16 -16
- data/spec/ollama/documents/redis_backed_memory_cache_spec.rb +95 -0
- data/spec/ollama/documents/redis_cache_spec.rb +15 -15
- data/spec/ollama/utils/fetcher_spec.rb +2 -1
- data/spec/ollama/utils/file_argument_spec.rb +17 -0
- data/spec/ollama/utils/tags_spec.rb +3 -3
- metadata +31 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 29863f68627a05541de5eab810850b0509db94c1d91b2e9a5a9be9bd387e8aea
|
4
|
+
data.tar.gz: 07f3558153a0a724c08e27a86482e0951070348bf25641d2a9feed113e3e7046
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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',
|
16
|
+
'.yardoc', 'doc', 'tags', 'errors.lst', 'cscope.out', 'coverage', 'tmp',
|
17
|
+
'corpus'
|
17
18
|
package_ignore '.all_images.yml', '.tool-versions', '.gitignore', 'VERSION',
|
18
|
-
'.
|
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::
|
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 =
|
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
|
-
|
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)
|
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
|
-
|
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:
|
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
|
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
|
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
|
631
|
+
clear_messages(messages)
|
605
632
|
puts "Cleared messages."
|
606
633
|
next
|
607
634
|
when %r(^/clobber$)
|
608
|
-
messages
|
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
|
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
|
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
|
-
|
699
|
-
|
700
|
-
|
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
|
-
|
28
|
+
count
|
27
29
|
end
|
28
30
|
|
29
31
|
def clear
|
30
|
-
@data.
|
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
|
40
|
-
|
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
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
data/lib/ollama/documents.rb
CHANGED
@@ -3,8 +3,11 @@ require 'digest'
|
|
3
3
|
|
4
4
|
class Ollama::Documents
|
5
5
|
end
|
6
|
-
|
7
|
-
|
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 =
|
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:
|
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
|
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
|
-
|
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
|
162
|
+
if cache_class.instance_method(:redis)
|
160
163
|
begin
|
161
164
|
cache = cache_class.new(prefix:)
|
162
165
|
cache.size
|
data/lib/ollama/utils/chooser.rb
CHANGED
@@ -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 ? "#{
|
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
|
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
|
[
|