ollama-ruby 0.11.0 → 0.12.1
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 +44 -1
- data/README.md +3 -2
- data/Rakefile +3 -1
- data/bin/ollama_chat +79 -29
- data/lib/ollama/client.rb +5 -5
- data/lib/ollama/documents/cache/common.rb +5 -1
- data/lib/ollama/documents/cache/memory_cache.rb +1 -1
- data/lib/ollama/documents/cache/records.rb +87 -0
- data/lib/ollama/documents/cache/redis_backed_memory_cache.rb +2 -1
- data/lib/ollama/documents/cache/redis_cache.rb +3 -10
- data/lib/ollama/documents/cache/sqlite_cache.rb +215 -0
- data/lib/ollama/documents/splitters/semantic.rb +1 -0
- data/lib/ollama/documents.rb +35 -62
- data/lib/ollama/utils/chooser.rb +15 -1
- data/lib/ollama/utils/tags.rb +2 -1
- data/lib/ollama/version.rb +1 -1
- data/ollama-ruby.gemspec +9 -7
- data/spec/ollama/documents/{memory_cache_spec.rb → cache/memory_cache_spec.rb} +37 -3
- data/spec/ollama/documents/{redis_backed_memory_cache_spec.rb → cache/redis_backed_memory_cache_spec.rb} +19 -7
- data/spec/ollama/documents/{redis_cache_spec.rb → cache/redis_cache_spec.rb} +34 -19
- data/spec/ollama/documents/cache/sqlite_cache_spec.rb +141 -0
- data/spec/ollama/utils/tags_spec.rb +7 -2
- metadata +50 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1a5d95ed76e20288199c655d7a48751f403db0228999a2703c50288a9f4816e
|
4
|
+
data.tar.gz: '0727293192245719c82619e8cd71423f7e477dfef194504585d7a5f756c96cca'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6c9f7ba3da2ba8f87182ee7acc53536a8b30fcc227a467b7d1cae8dee14d8b7258ad3e97b7960d9a079cfa2d28b5a1dea2a5776a27a1119a7515f515d0f5c3e
|
7
|
+
data.tar.gz: 753a7812dbf850e21f042f8cbccec96163f21c7766a6a54d552cc0de98ba0581133140b5be9f11ae55a5faf98ea71101e13280bc673dd3ee67f2c26945e4005f
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,48 @@
|
|
1
1
|
# Changes
|
2
2
|
|
3
|
+
## 2024-11-27 v0.12.1
|
4
|
+
|
5
|
+
* Added handling for empty links list:
|
6
|
+
+ If the list is empty, print a message indicating that the list is empty.
|
7
|
+
|
8
|
+
## 2024-11-26 v0.12.0
|
9
|
+
|
10
|
+
* **Upgrade display/clear links used in chat**:
|
11
|
+
* Created `$links` set to store used links.
|
12
|
+
* Added `/links` command to display used links as a enumerated list.
|
13
|
+
* Implemented `/links (clear)` feature to remove all or specific used links.
|
14
|
+
* **Update semantic splitter to handle embeddings size < 2**:
|
15
|
+
+ Added condition to return sentences directly when embeddings size is less
|
16
|
+
than 2.
|
17
|
+
* **Removed collection list from chat info output**
|
18
|
+
* **Add SQLiteCache spec for convert_to_vector method**:
|
19
|
+
- Test creates a vector with two elements and checks if
|
20
|
+
`cache.convert_to_vector(vector)` returns the same vector (which for this
|
21
|
+
cache is just a Ruby array).
|
22
|
+
* **Add tests for retrieving tags from cache**:
|
23
|
+
* Test if tags are returned as an instance of `Ollama::Utils::Tags`
|
24
|
+
* Test also checks if the order of the tags is correct
|
25
|
+
* **Added test case for clearing tags from `Ollama::Documents::SQLiteCache`**
|
26
|
+
- Updated spec for new `clear_for_tags` method
|
27
|
+
* **Migrate SQLite cache to use new clear_for_tags method**:
|
28
|
+
+ Added `clear_for_tags` method to SQLiteCache class in `sqlite_cache.rb`
|
29
|
+
+ Modified `clear` method in `records.rb` to call `clear_for_tags` if
|
30
|
+
available
|
31
|
+
+ Created `find_records_for_tags` method in `sqlite_cache.rb` to find records
|
32
|
+
by tags
|
33
|
+
+ Updated `find_records` method in `sqlite_cache.rb` to use new
|
34
|
+
`find_records_for_tags` method
|
35
|
+
* **Use Ollama::Utils::Tags for consistently handling tags**
|
36
|
+
* **Upgrade SQLite cache to use correct prefix for full_each**:
|
37
|
+
* Use `?%` as the default prefix in `SQLiteCache#full_each`
|
38
|
+
* Add specs for setting keys with different prefixes in `SQLiteCache`
|
39
|
+
* Add specs for setting keys with different prefixes in `MemoryCache`
|
40
|
+
* **Refactor SQLite cache query explanation**
|
41
|
+
+ Use new variable `e` to store sanitized query for debugging purposes
|
42
|
+
+ Pass sanitized query `e` to `@database.execute` for `EXPLAIN` instead of
|
43
|
+
original query `a[0]`
|
44
|
+
* **Add test for unique tags with leading # characters**
|
45
|
+
|
3
46
|
## 2024-11-20 v0.11.0
|
4
47
|
|
5
48
|
* Added `voice` and `interactive` reader attributes to the Say handler class.
|
@@ -25,7 +68,7 @@
|
|
25
68
|
+ Added support for `file://` protocol to content scans.
|
26
69
|
+ Updated regex pattern to match local files starting with `~`, `.`, or `/`.
|
27
70
|
+ Remove # anchors for file URLs (and files)
|
28
|
-
* Improved parsing of content in ollama_chat
|
71
|
+
* Improved parsing of content in `ollama_chat`:
|
29
72
|
+ Use `content.scan(%r((https?://\S+)|(#\S+)|(\S+\/\S+)))` to match URLs, tags and files.
|
30
73
|
+ For foo/bar file pathes prepend `./`foo/bar, for foo you have to enter ./foo still.
|
31
74
|
+ Added a check for file existence before fetching its content
|
data/README.md
CHANGED
@@ -44,8 +44,8 @@ print ollama.chat(model: 'llama3.1', stream: true, messages:).lazy.map { |respon
|
|
44
44
|
|
45
45
|
## Try out things in ollama\_console
|
46
46
|
|
47
|
-
This is an interactive console
|
48
|
-
provided by an `Ollama::Client` instance. For example this command
|
47
|
+
This is an interactive console where you can try out the different commands
|
48
|
+
provided by an `Ollama::Client` instance. For example, this command generates a
|
49
49
|
response and displays it on the screen using the Markdown handler:
|
50
50
|
|
51
51
|
```
|
@@ -459,6 +459,7 @@ The following commands can be given inside the chat, if prefixed by a `/`:
|
|
459
459
|
/embedding toggle embedding paused or not
|
460
460
|
/embed source embed the source's content
|
461
461
|
/web [n] query query web search & return n or 1 results
|
462
|
+
/links( clear) display (or clear) links used in the chat
|
462
463
|
/save filename store conversation messages
|
463
464
|
/load filename load conversation messages
|
464
465
|
/quit to quit
|
data/Rakefile
CHANGED
@@ -42,7 +42,9 @@ GemHadar do
|
|
42
42
|
dependency 'tins', '~> 1.34'
|
43
43
|
dependency 'kramdown-ansi', '~> 0.0', '>= 0.0.1'
|
44
44
|
dependency 'ostruct', '~> 0.0'
|
45
|
-
|
45
|
+
dependency 'sqlite-vec', '~> 0.0'
|
46
|
+
dependency 'sqlite3', '~> 2.0', '>= 2.0.1'
|
47
|
+
development_dependency 'all_images', '~> 0.6'
|
46
48
|
development_dependency 'rspec', '~> 3.2'
|
47
49
|
development_dependency 'webmock'
|
48
50
|
development_dependency 'debug'
|
data/bin/ollama_chat
CHANGED
@@ -57,13 +57,15 @@ class OllamaChatConfig
|
|
57
57
|
enabled: true
|
58
58
|
model:
|
59
59
|
name: mxbai-embed-large
|
60
|
+
embedding_length: 1024
|
60
61
|
options: {}
|
61
62
|
# Retrieval prompt template:
|
62
63
|
prompt: 'Represent this sentence for searching relevant passages: %s'
|
63
64
|
batch_size: 10
|
65
|
+
database_filename: null # ':memory:'
|
64
66
|
collection: <%= ENV['OLLAMA_CHAT_COLLECTION'] %>
|
65
67
|
found_texts_size: 4096
|
66
|
-
found_texts_count:
|
68
|
+
found_texts_count: 10
|
67
69
|
splitter:
|
68
70
|
name: RecursiveCharacter
|
69
71
|
chunk_size: 1024
|
@@ -81,12 +83,15 @@ class OllamaChatConfig
|
|
81
83
|
|
82
84
|
def initialize(filename = nil)
|
83
85
|
@filename = filename || default_path
|
86
|
+
unless File.directory?(cache_dir_path)
|
87
|
+
mkdir_p cache_dir_path.to_s
|
88
|
+
end
|
84
89
|
@config = Provider.config(@filename, '⚙️')
|
85
90
|
retried = false
|
86
91
|
rescue ConfigurationFileMissing
|
87
92
|
if @filename == default_path && !retried
|
88
93
|
retried = true
|
89
|
-
mkdir_p
|
94
|
+
mkdir_p config_dir_path.to_s
|
90
95
|
File.secure_write(default_path, DEFAULT_CONFIG)
|
91
96
|
retry
|
92
97
|
else
|
@@ -105,6 +110,14 @@ class OllamaChatConfig
|
|
105
110
|
def config_dir_path
|
106
111
|
XDG.new.config_home + 'ollama_chat'
|
107
112
|
end
|
113
|
+
|
114
|
+
def cache_dir_path
|
115
|
+
XDG.new.cache_home + 'ollama_chat'
|
116
|
+
end
|
117
|
+
|
118
|
+
def database_path
|
119
|
+
cache_dir_path + 'documents.db'
|
120
|
+
end
|
108
121
|
end
|
109
122
|
|
110
123
|
class FollowChat
|
@@ -316,11 +329,12 @@ def search_web(query, n = nil)
|
|
316
329
|
doc.css('.results_links').each do |link|
|
317
330
|
if n > 0
|
318
331
|
url = link.css('.result__a').first&.[]('href')
|
319
|
-
url.sub!(%r(\A
|
332
|
+
url.sub!(%r(\A(//duckduckgo\.com)?/l/\?uddg=), '')
|
320
333
|
url.sub!(%r(&rut=.*), '')
|
321
334
|
url = URI.decode_uri_component(url)
|
322
335
|
url = URI.parse(url)
|
323
336
|
url.host =~ /duckduckgo\.com/ and next
|
337
|
+
$links.add(url.to_s)
|
324
338
|
result << url
|
325
339
|
n -= 1
|
326
340
|
else
|
@@ -387,7 +401,7 @@ module SourceParsing
|
|
387
401
|
end
|
388
402
|
source_io.rewind
|
389
403
|
source_io.read
|
390
|
-
when 'text/csv'
|
404
|
+
when 'text/csv'
|
391
405
|
parse_csv(source_io)
|
392
406
|
when 'application/rss+xml'
|
393
407
|
parse_rss(source_io)
|
@@ -510,6 +524,7 @@ def fetch_source(source, &block)
|
|
510
524
|
block.(tmp)
|
511
525
|
end
|
512
526
|
when %r(\Ahttps?://\S+)
|
527
|
+
$links.add(source.to_s)
|
513
528
|
Utils::Fetcher.get(
|
514
529
|
source,
|
515
530
|
cache: $cache,
|
@@ -603,7 +618,6 @@ def embed_source(source_io, source, count: nil)
|
|
603
618
|
percentage: splitter_config.percentage?,
|
604
619
|
percentile: splitter_config.percentile?,
|
605
620
|
)
|
606
|
-
inputs = splitter.split(text)
|
607
621
|
end
|
608
622
|
inputs or return
|
609
623
|
source = source.to_s
|
@@ -624,7 +638,6 @@ def embed(source)
|
|
624
638
|
content.present? or return
|
625
639
|
source_io.rewind
|
626
640
|
embed_source(source_io, source)
|
627
|
-
content
|
628
641
|
end
|
629
642
|
$config.prompts.embed % { source: }
|
630
643
|
else
|
@@ -649,6 +662,7 @@ def parse_content(content, images)
|
|
649
662
|
File.exist?(file) or next
|
650
663
|
source = file
|
651
664
|
when url
|
665
|
+
$links.add(url.to_s)
|
652
666
|
source = url
|
653
667
|
end
|
654
668
|
fetch_source(source) do |source_io|
|
@@ -674,7 +688,7 @@ def parse_content(content, images)
|
|
674
688
|
end
|
675
689
|
end
|
676
690
|
end
|
677
|
-
new_content = contents.select
|
691
|
+
new_content = contents.select { _1.present? rescue nil }.compact * "\n\n"
|
678
692
|
return new_content, (tags unless tags.empty?)
|
679
693
|
end
|
680
694
|
|
@@ -735,17 +749,12 @@ ensure
|
|
735
749
|
end
|
736
750
|
|
737
751
|
def collection_stats
|
738
|
-
list = $documents.collections.sort.map { |c|
|
739
|
-
' ' + ($documents.collection == c ? bold { c } : c).to_s
|
740
|
-
}.join(?\n)
|
741
752
|
puts <<~EOT
|
742
753
|
Current Collection
|
743
754
|
Name: #{bold{$documents.collection}}
|
744
755
|
#Embeddings: #{$documents.size}
|
745
756
|
#Tags: #{$documents.tags.size}
|
746
757
|
Tags: #{$documents.tags}
|
747
|
-
List:
|
748
|
-
#{list}
|
749
758
|
EOT
|
750
759
|
end
|
751
760
|
|
@@ -785,10 +794,11 @@ def set_system_prompt(messages, system)
|
|
785
794
|
messages << Message.new(role: 'system', content: system)
|
786
795
|
end
|
787
796
|
|
788
|
-
def change_system_prompt(messages, default)
|
789
|
-
|
790
|
-
|
791
|
-
|
797
|
+
def change_system_prompt(messages, default, system: nil)
|
798
|
+
selector = Regexp.new(system.to_s[1..-1].to_s)
|
799
|
+
prompts = $config.system_prompts.attribute_names.compact.grep(selector)
|
800
|
+
chosen = Utils::Chooser.choose(prompts, return_immediately: true)
|
801
|
+
system = if chosen
|
792
802
|
$config.system_prompts.send(chosen)
|
793
803
|
else
|
794
804
|
default
|
@@ -869,6 +879,7 @@ def display_chat_help
|
|
869
879
|
/embedding toggle embedding paused or not
|
870
880
|
/embed source embed the source's content
|
871
881
|
/web [n] query query web search & return n or 1 results
|
882
|
+
/links( clear) display (or clear) links used in the chat
|
872
883
|
/save filename store conversation messages
|
873
884
|
/load filename load conversation messages
|
874
885
|
/quit to quit
|
@@ -907,16 +918,17 @@ end
|
|
907
918
|
|
908
919
|
$opts = go 'f:u:m:s:c:C:D:MEVh'
|
909
920
|
|
910
|
-
|
911
|
-
$config
|
921
|
+
$ollama_chat_config = OllamaChatConfig.new($opts[?f])
|
922
|
+
$config = $ollama_chat_config.config
|
912
923
|
|
913
924
|
setup_switches
|
914
925
|
|
915
926
|
$opts[?h] and usage
|
916
927
|
$opts[?V] and version
|
917
928
|
|
918
|
-
base_url
|
919
|
-
|
929
|
+
base_url = $opts[?u] || $config.url
|
930
|
+
user_agent = [ File.basename($0), Ollama::VERSION ] * ?/
|
931
|
+
$ollama = Client.new(base_url:, debug: $config.debug, user_agent:)
|
920
932
|
|
921
933
|
$document_policy = $config.document_policy
|
922
934
|
$model = choose_model($opts[?m], $config.model.name)
|
@@ -929,8 +941,8 @@ if $opts[?c]
|
|
929
941
|
messages.concat load_conversation($opts[?c])
|
930
942
|
else
|
931
943
|
default = $config.system_prompts.default? || model_system
|
932
|
-
if $opts[?s]
|
933
|
-
change_system_prompt(messages, default)
|
944
|
+
if $opts[?s] =~ /\A\?/
|
945
|
+
change_system_prompt(messages, default, system: $opts[?s])
|
934
946
|
else
|
935
947
|
system = Utils::FileArgument.get_file_argument($opts[?s], default:)
|
936
948
|
system.present? and set_system_prompt(messages, system)
|
@@ -944,12 +956,13 @@ if $embedding.on?
|
|
944
956
|
collection = $opts[?C] || $config.embedding.collection
|
945
957
|
$documents = Documents.new(
|
946
958
|
ollama:,
|
947
|
-
model:
|
948
|
-
model_options:
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
959
|
+
model: $embedding_model,
|
960
|
+
model_options: $config.embedding.model.options,
|
961
|
+
database_filename: $config.embedding.database_filename || $ollama_chat_config.database_path,
|
962
|
+
collection: ,
|
963
|
+
cache: configure_cache,
|
964
|
+
redis_url: $config.redis.documents.url?,
|
965
|
+
debug: $config.debug
|
953
966
|
)
|
954
967
|
|
955
968
|
document_list = $opts[?D].to_a
|
@@ -991,10 +1004,11 @@ end
|
|
991
1004
|
|
992
1005
|
$current_voice = $config.voice.default
|
993
1006
|
|
994
|
-
puts "Configuration read from #{
|
1007
|
+
puts "Configuration read from #{$ollama_chat_config.filename.inspect} is:", $config
|
995
1008
|
info
|
996
1009
|
puts "\nType /help to display the chat help."
|
997
1010
|
|
1011
|
+
$links = Set.new
|
998
1012
|
images = []
|
999
1013
|
loop do
|
1000
1014
|
parse_content = true
|
@@ -1134,6 +1148,42 @@ loop do
|
|
1134
1148
|
save_conversation($1, messages)
|
1135
1149
|
puts "Saved conversation to #$1."
|
1136
1150
|
next
|
1151
|
+
when %r(^/links(?:\s+(clear))?$)
|
1152
|
+
case $1
|
1153
|
+
when 'clear'
|
1154
|
+
loop do
|
1155
|
+
links = $links.dup.add('[EXIT]').add('[ALL]')
|
1156
|
+
link = Utils::Chooser.choose(links, prompt: 'Clear? %s')
|
1157
|
+
case link
|
1158
|
+
when nil, '[EXIT]'
|
1159
|
+
puts "Exiting chooser."
|
1160
|
+
break
|
1161
|
+
when '[ALL]'
|
1162
|
+
if ask?(prompt: 'Are you sure? (y/n) ') =~ /\Ay/i
|
1163
|
+
$links.clear
|
1164
|
+
puts "Cleared all links in list."
|
1165
|
+
break
|
1166
|
+
else
|
1167
|
+
puts 'Cancelled.'
|
1168
|
+
sleep 3
|
1169
|
+
end
|
1170
|
+
when /./
|
1171
|
+
$links.delete(link)
|
1172
|
+
puts "Cleared link from links in list."
|
1173
|
+
sleep 3
|
1174
|
+
end
|
1175
|
+
end
|
1176
|
+
when nil
|
1177
|
+
if $links.empty?
|
1178
|
+
puts "List is empty."
|
1179
|
+
else
|
1180
|
+
Math.log10($links.size).ceil
|
1181
|
+
format = "% #{}s. %s"
|
1182
|
+
connect = -> link { hyperlink(link) { link } }
|
1183
|
+
puts $links.each_with_index.map { |x, i| format % [ i + 1, connect.(x) ] }
|
1184
|
+
end
|
1185
|
+
end
|
1186
|
+
next
|
1137
1187
|
when %r(^/load\s+(.+)$)
|
1138
1188
|
messages = load_conversation($1)
|
1139
1189
|
puts "Loaded conversation from #$1."
|
data/lib/ollama/client.rb
CHANGED
@@ -10,7 +10,7 @@ class Ollama::Client
|
|
10
10
|
|
11
11
|
annotate :doc
|
12
12
|
|
13
|
-
def initialize(base_url: nil, output: $stdout, connect_timeout: nil, read_timeout: nil, write_timeout: nil, debug: nil)
|
13
|
+
def initialize(base_url: nil, output: $stdout, connect_timeout: nil, read_timeout: nil, write_timeout: nil, debug: nil, user_agent: nil)
|
14
14
|
base_url.nil? and base_url = ENV.fetch('OLLAMA_URL') do
|
15
15
|
raise ArgumentError,
|
16
16
|
'missing :base_url parameter or OLLAMA_URL environment variable'
|
@@ -21,8 +21,8 @@ class Ollama::Client
|
|
21
21
|
@ssl_verify_peer = base_url.query.to_s.split(?&).inject({}) { |h, l|
|
22
22
|
h.merge Hash[*l.split(?=)]
|
23
23
|
}['ssl_verify_peer'] != 'false'
|
24
|
-
@base_url, @output, @connect_timeout, @read_timeout, @write_timeout, @debug =
|
25
|
-
base_url, output, connect_timeout, read_timeout, write_timeout, debug
|
24
|
+
@base_url, @output, @connect_timeout, @read_timeout, @write_timeout, @debug, @user_agent =
|
25
|
+
base_url, output, connect_timeout, read_timeout, write_timeout, debug, user_agent
|
26
26
|
end
|
27
27
|
|
28
28
|
attr_accessor :output
|
@@ -111,13 +111,13 @@ class Ollama::Client
|
|
111
111
|
|
112
112
|
def headers
|
113
113
|
{
|
114
|
-
'User-Agent' => self.class.user_agent,
|
114
|
+
'User-Agent' => @user_agent || self.class.user_agent,
|
115
115
|
'Content-Type' => 'application/json; charset=utf-8',
|
116
116
|
}
|
117
117
|
end
|
118
118
|
|
119
119
|
def self.user_agent
|
120
|
-
'%s/%s' % [ self
|
120
|
+
'%s/%s' % [ self, Ollama::VERSION ]
|
121
121
|
end
|
122
122
|
|
123
123
|
def excon(url)
|
@@ -1,7 +1,11 @@
|
|
1
1
|
module Ollama::Documents::Cache::Common
|
2
2
|
include Ollama::Utils::Math
|
3
3
|
|
4
|
-
|
4
|
+
def initialize(prefix:)
|
5
|
+
self.prefix = prefix
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_accessor :prefix # current prefix defined for the cache
|
5
9
|
|
6
10
|
# Returns an array of collection names that match the given prefix.
|
7
11
|
#
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Ollama::Documents::Cache::Records
|
2
|
+
class Record < JSON::GenericObject
|
3
|
+
def initialize(*a)
|
4
|
+
super
|
5
|
+
self.text ||= ''
|
6
|
+
self.norm ||= 0.0
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
my_tags = tags_set
|
11
|
+
my_tags.empty? or my_tags = " #{my_tags}"
|
12
|
+
"#<#{self.class} #{text.inspect}#{my_tags} #{similarity || 'n/a'}>"
|
13
|
+
end
|
14
|
+
|
15
|
+
def tags_set
|
16
|
+
Ollama::Utils::Tags.new(tags, source:)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
text == other.text
|
21
|
+
end
|
22
|
+
|
23
|
+
alias inspect to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
module RedisFullEach
|
27
|
+
def full_each(&block)
|
28
|
+
redis.scan_each(match: [ Ollama::Documents, ?* ] * ?-) do |key|
|
29
|
+
value = redis.get(key) or next
|
30
|
+
value = JSON(value, object_class: Ollama::Documents::Record)
|
31
|
+
block.(key, value)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module FindRecords
|
37
|
+
def find_records(needle, tags: nil, max_records: nil)
|
38
|
+
tags = Ollama::Utils::Tags.new(Array(tags)).to_a
|
39
|
+
records = self
|
40
|
+
if tags.present?
|
41
|
+
records = records.select { |_key, record| (tags & record.tags).size >= 1 }
|
42
|
+
end
|
43
|
+
needle_norm = norm(needle)
|
44
|
+
records = records.sort_by { |key, record|
|
45
|
+
record.key = key
|
46
|
+
record.similarity = cosine_similarity(
|
47
|
+
a: needle,
|
48
|
+
b: record.embedding,
|
49
|
+
a_norm: needle_norm,
|
50
|
+
b_norm: record.norm,
|
51
|
+
)
|
52
|
+
}
|
53
|
+
records.transpose.last&.reverse.to_a
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module Tags
|
58
|
+
def clear(tags: nil)
|
59
|
+
tags = Ollama::Utils::Tags.new(tags).to_a
|
60
|
+
if tags.present?
|
61
|
+
if respond_to?(:clear_for_tags)
|
62
|
+
clear_for_tags(tags)
|
63
|
+
else
|
64
|
+
each do |key, record|
|
65
|
+
if (tags & record.tags.to_a).size >= 1
|
66
|
+
delete(unpre(key))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
else
|
71
|
+
super()
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def tags
|
76
|
+
if defined? super
|
77
|
+
super
|
78
|
+
else
|
79
|
+
each_with_object(Ollama::Utils::Tags.new) do |(_, record), t|
|
80
|
+
record.tags.each do |tag|
|
81
|
+
t.add(tag, source: record.source)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -5,8 +5,9 @@ class Ollama::Documents
|
|
5
5
|
def initialize(prefix:, url: ENV['REDIS_URL'], object_class: nil)
|
6
6
|
super(prefix:)
|
7
7
|
url or raise ArgumentError, 'require redis url'
|
8
|
-
@
|
8
|
+
@url, @object_class = url, object_class
|
9
9
|
@redis_cache = Ollama::Documents::RedisCache.new(prefix:, url:, object_class:)
|
10
|
+
@redis_cache.extend(Ollama::Documents::Cache::Records::RedisFullEach)
|
10
11
|
@redis_cache.full_each do |key, value|
|
11
12
|
@data[key] = value
|
12
13
|
end
|
@@ -5,8 +5,9 @@ class Ollama::Documents::RedisCache
|
|
5
5
|
include Ollama::Documents::Cache::Common
|
6
6
|
|
7
7
|
def initialize(prefix:, url: ENV['REDIS_URL'], object_class: nil, ex: nil)
|
8
|
+
super(prefix:)
|
8
9
|
url or raise ArgumentError, 'require redis url'
|
9
|
-
@
|
10
|
+
@url, @object_class, @ex = url, object_class, ex
|
10
11
|
end
|
11
12
|
|
12
13
|
attr_reader :object_class
|
@@ -18,7 +19,7 @@ class Ollama::Documents::RedisCache
|
|
18
19
|
def [](key)
|
19
20
|
value = redis.get(pre(key))
|
20
21
|
unless value.nil?
|
21
|
-
JSON(value, object_class:)
|
22
|
+
object_class ? JSON(value, object_class:) : JSON(value)
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
@@ -64,12 +65,4 @@ class Ollama::Documents::RedisCache
|
|
64
65
|
self
|
65
66
|
end
|
66
67
|
include Enumerable
|
67
|
-
|
68
|
-
def full_each(&block)
|
69
|
-
redis.scan_each(match: [ Ollama::Documents, ?* ] * ?-) do |key|
|
70
|
-
value = redis.get(key) or next
|
71
|
-
value = JSON(value, object_class: Ollama::Documents::Record)
|
72
|
-
block.(key, value)
|
73
|
-
end
|
74
|
-
end
|
75
68
|
end
|