ollama-ruby 0.12.1 → 0.14.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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/CHANGES.md +39 -0
  4. data/README.md +70 -144
  5. data/Rakefile +5 -17
  6. data/bin/ollama_cli +37 -6
  7. data/lib/ollama/client/command.rb +2 -2
  8. data/lib/ollama/dto.rb +4 -0
  9. data/lib/ollama/version.rb +1 -1
  10. data/lib/ollama.rb +0 -11
  11. data/ollama-ruby.gemspec +11 -22
  12. data/spec/ollama/message_spec.rb +9 -0
  13. metadata +25 -255
  14. data/bin/ollama_chat +0 -1248
  15. data/config/redis.conf +0 -5
  16. data/docker-compose.yml +0 -10
  17. data/lib/ollama/documents/cache/common.rb +0 -36
  18. data/lib/ollama/documents/cache/memory_cache.rb +0 -44
  19. data/lib/ollama/documents/cache/records.rb +0 -87
  20. data/lib/ollama/documents/cache/redis_backed_memory_cache.rb +0 -39
  21. data/lib/ollama/documents/cache/redis_cache.rb +0 -68
  22. data/lib/ollama/documents/cache/sqlite_cache.rb +0 -215
  23. data/lib/ollama/documents/splitters/character.rb +0 -72
  24. data/lib/ollama/documents/splitters/semantic.rb +0 -91
  25. data/lib/ollama/documents.rb +0 -184
  26. data/lib/ollama/utils/cache_fetcher.rb +0 -38
  27. data/lib/ollama/utils/chooser.rb +0 -52
  28. data/lib/ollama/utils/colorize_texts.rb +0 -65
  29. data/lib/ollama/utils/fetcher.rb +0 -175
  30. data/lib/ollama/utils/file_argument.rb +0 -34
  31. data/lib/ollama/utils/math.rb +0 -48
  32. data/lib/ollama/utils/tags.rb +0 -67
  33. data/spec/assets/embeddings.json +0 -1
  34. data/spec/assets/prompt.txt +0 -1
  35. data/spec/ollama/documents/cache/memory_cache_spec.rb +0 -97
  36. data/spec/ollama/documents/cache/redis_backed_memory_cache_spec.rb +0 -118
  37. data/spec/ollama/documents/cache/redis_cache_spec.rb +0 -121
  38. data/spec/ollama/documents/cache/sqlite_cache_spec.rb +0 -141
  39. data/spec/ollama/documents/splitters/character_spec.rb +0 -110
  40. data/spec/ollama/documents/splitters/semantic_spec.rb +0 -56
  41. data/spec/ollama/documents_spec.rb +0 -162
  42. data/spec/ollama/utils/cache_fetcher_spec.rb +0 -43
  43. data/spec/ollama/utils/colorize_texts_spec.rb +0 -13
  44. data/spec/ollama/utils/fetcher_spec.rb +0 -137
  45. data/spec/ollama/utils/file_argument_spec.rb +0 -17
  46. data/spec/ollama/utils/tags_spec.rb +0 -53
@@ -1,162 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Ollama::Documents do
4
- let :ollama do
5
- double('Ollama::Client')
6
- end
7
-
8
- let :model do
9
- 'mxbai-embed-large'
10
- end
11
-
12
- let :documents do
13
- described_class.new ollama:, model:
14
- end
15
-
16
- it 'can be instantiated' do
17
- expect(documents).to be_a described_class
18
- end
19
-
20
- it 'no texts can be added to it' do
21
- expect(documents.add([])).to eq documents
22
- end
23
-
24
- it 'texts can be added to it' do
25
- expect(ollama).to receive(:embed).
26
- with(model:, input: %w[ foo bar ], options: nil).
27
- and_return(double(embeddings: [ [ 0.1 ], [ 0.2 ] ]))
28
- expect(documents.add(%w[ foo bar ])).to eq documents
29
- expect(documents.exist?('foo')).to eq true
30
- expect(documents.exist?('bar')).to eq true
31
- expect(documents['foo']).to be_a Ollama::Documents::Record
32
- end
33
-
34
- it 'a text can be added to it' do
35
- expect(ollama).to receive(:embed).
36
- with(model:, input: %w[ foo ], options: nil).
37
- and_return(double(embeddings: [ [ 0.1 ] ]))
38
- expect(documents << 'foo').to eq documents
39
- expect(documents.exist?('foo')).to eq true
40
- expect(documents.exist?('bar')).to eq false
41
- expect(documents['foo']).to be_a Ollama::Documents::Record
42
- end
43
-
44
- it 'can find strings' do
45
- expect(ollama).to receive(:embed).
46
- with(model:, input: [ 'foo' ], options: nil).
47
- and_return(double(embeddings: [ [ 0.1 ] ]))
48
- expect(documents << 'foo').to eq documents
49
- expect(ollama).to receive(:embed).
50
- with(model:, input: 'foo', options: nil).
51
- and_return(double(embeddings: [ [ 0.1 ] ]))
52
- records = documents.find('foo')
53
- expect(records).to eq [
54
- Ollama::Documents::Record[text: 'foo', embedding: [ 0.1 ], similarity: 1.0 ]
55
- ]
56
- expect(records[0].to_s).to eq '#<Ollama::Documents::Record "foo" 1.0>'
57
- end
58
-
59
- it 'can find only tagged strings' do
60
- expect(ollama).to receive(:embed).
61
- with(model:, input: [ 'foo' ], options: nil).
62
- and_return(double(embeddings: [ [ 0.1 ] ]))
63
- expect(documents.add('foo', tags: %w[ test ])).to eq documents
64
- expect(ollama).to receive(:embed).
65
- with(model:, input: 'foo', options: nil).
66
- and_return(double(embeddings: [ [ 0.1 ] ]))
67
- records = documents.find('foo', tags: %w[ nix ])
68
- expect(records).to eq []
69
- expect(ollama).to receive(:embed).
70
- with(model:, input: 'foo', options: nil).
71
- and_return(double(embeddings: [ [ 0.1 ] ]))
72
- records = documents.find('foo', tags: %w[ test ])
73
- expect(records).to eq [
74
- Ollama::Documents::Record[text: 'foo', embedding: [ 0.1 ], similarity: 1.0 ]
75
- ]
76
- expect(records[0].to_s).to eq '#<Ollama::Documents::Record "foo" #test 1.0>'
77
- end
78
-
79
- it 'can find strings conditionally' do
80
- expect(ollama).to receive(:embed).
81
- with(model:, input: [ 'foobar' ], options: nil).
82
- and_return(double(embeddings: [ [ 0.01 ] ]))
83
- expect(ollama).to receive(:embed).
84
- with(model:, input: [ 'foo' ], options: nil).
85
- and_return(double(embeddings: [ [ 0.1 ] ]))
86
- expect(documents << 'foobar').to eq documents
87
- expect(documents << 'foo').to eq documents
88
- expect(ollama).to receive(:embed).at_least(:once).
89
- with(model:, input: 'foo', options: nil).
90
- and_return(double(embeddings: [ [ 0.1 ] ]))
91
- records = documents.find_where('foo', text_count: 1)
92
- expect(records).to eq [
93
- Ollama::Documents::Record[text: 'foo', embedding: [ 0.1 ], similarity: 1.0 ],
94
- ]
95
- records = documents.find_where('foo', text_size: 3)
96
- expect(records).to eq [
97
- Ollama::Documents::Record[text: 'foo', embedding: [ 0.1 ], similarity: 1.0 ],
98
- ]
99
- records = documents.find_where('foo')
100
- expect(records).to eq [
101
- Ollama::Documents::Record[text: 'foo', embedding: [ 0.1 ], similarity: 1.0 ],
102
- Ollama::Documents::Record[text: 'foobar', embedding: [ 0.1 ], similarity: 1.0 ],
103
- ]
104
- end
105
-
106
-
107
- context 'it uses cache' do
108
- before do
109
- allow(ollama).to receive(:embed).
110
- with(model:, input: %w[ foo ], options: nil).
111
- and_return(double(embeddings: [ [ 0.1 ] ]))
112
- end
113
-
114
- it 'can delete texts' do
115
- expect(documents << 'foo').to eq documents
116
- expect {
117
- documents.delete('foo')
118
- }.to change { documents.exist?('foo') }.from(true).to(false)
119
- end
120
-
121
- it 'tracks size' do
122
- expect {
123
- expect(documents << 'foo').to eq documents
124
- }.to change { documents.size }.from(0).to(1)
125
- end
126
-
127
- it 'can clear texts' do
128
- expect(documents << 'foo').to eq documents
129
- expect {
130
- documents.clear
131
- }.to change { documents.size }.from(1).to(0)
132
- end
133
-
134
- it 'can clear texts with tags' do
135
- expect(ollama).to receive(:embed).
136
- with(model:, input: %w[ bar ], options: nil).
137
- and_return(double(embeddings: [ [ 0.1 ] ]))
138
- expect(documents.add('foo', tags: %w[ test ])).to eq documents
139
- expect(documents.add('bar', tags: %w[ test2 ])).to eq documents
140
- expect(documents.tags.to_a).to eq %w[ test test2 ]
141
- expect {
142
- documents.clear tags: 'test'
143
- }.to change { documents.size }.from(2).to(1)
144
- expect {
145
- documents.clear tags: :test2
146
- }.to change { documents.size }.from(1).to(0)
147
- end
148
-
149
- it 'returns collections' do
150
- expect(documents.collections).to eq [ :default ]
151
- end
152
-
153
- it 'can change collection' do
154
- expect(documents.instance_eval { @cache }).to receive(:prefix=).
155
- with(/#@collection/).and_call_original
156
- expect { documents.collection = :new_collection }.
157
- to change { documents.collection }.
158
- from(:default).
159
- to(:new_collection)
160
- end
161
- end
162
- end
@@ -1,43 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Ollama::Utils::CacheFetcher do
4
- let :url do
5
- 'https://www.example.com/hello'
6
- end
7
-
8
- let :cache do
9
- double('RedisCache')
10
- end
11
-
12
- let :fetcher do
13
- described_class.new(cache).expose
14
- end
15
-
16
- it 'can be instantiated' do
17
- expect(fetcher).to be_a described_class
18
- end
19
-
20
- it 'has #get' do
21
- expect(cache).to receive(:[]).with('body-69ce405ab83f42dffa9fd22bbd47783f').and_return 'world'
22
- expect(cache).to receive(:[]).with('content_type-69ce405ab83f42dffa9fd22bbd47783f').and_return 'text/plain'
23
- yielded_io = nil
24
- block = -> io { yielded_io = io }
25
- fetcher.get(url, &block)
26
- expect(yielded_io).to be_a StringIO
27
- expect(yielded_io.read).to eq 'world'
28
- end
29
-
30
- it '#get needs block' do
31
- expect { fetcher.get(url) }.to raise_error(ArgumentError)
32
- end
33
-
34
- it 'has #put' do
35
- io = StringIO.new('world')
36
- io.extend(Ollama::Utils::Fetcher::HeaderExtension)
37
- io.content_type = MIME::Types['text/plain'].first
38
- io.ex = 666
39
- expect(cache).to receive(:set).with('body-69ce405ab83f42dffa9fd22bbd47783f', 'world', ex: 666)
40
- expect(cache).to receive(:set).with('content_type-69ce405ab83f42dffa9fd22bbd47783f', 'text/plain', ex: 666)
41
- fetcher.put(url, io)
42
- end
43
- end
@@ -1,13 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Ollama::Utils::ColorizeTexts do
4
- it 'colorizes texts' do
5
- ct = described_class.new(%w[ foo bar ])
6
- colored = ct.to_s
7
- uncolored = Term::ANSIColor.uncolor(ct.to_s)
8
- expect(colored.size).to be > uncolored.size
9
- expect(uncolored).to eq(
10
- "foo\n#3 \n\nbar\n#3 \n\n"
11
- )
12
- end
13
- end
@@ -1,137 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Ollama::Utils::Fetcher do
4
- let :url do
5
- 'https://www.example.com/hello'
6
- end
7
-
8
- let :fetcher do
9
- described_class.new.expose
10
- end
11
-
12
- it 'can be instantiated' do
13
- expect(fetcher).to be_a described_class
14
- end
15
-
16
- it 'has .get' do
17
- expect(described_class).to receive(:new).and_return double(get: true)
18
- described_class.get(url)
19
- end
20
-
21
- it 'can #get with streaming' do
22
- stub_request(:get, url).
23
- with(headers: fetcher.headers).
24
- to_return(
25
- status: 200,
26
- body: 'world',
27
- headers: { 'Content-Type' => 'text/plain' },
28
- )
29
- fetcher.get(url) do |tmp|
30
- expect(tmp).to be_a Tempfile
31
- expect(tmp.read).to eq 'world'
32
- expect(tmp.content_type).to eq 'text/plain'
33
- end
34
- end
35
-
36
- it 'can #get without ssl peer verification' do
37
- fetcher = described_class.new(
38
- http_options: { ssl_verify_peer: false }
39
- ).expose
40
- stub_request(:get, url).
41
- with(headers: fetcher.headers).
42
- to_return(
43
- status: 200,
44
- body: 'world',
45
- headers: { 'Content-Type' => 'text/plain' },
46
- )
47
- expect(Excon).to receive(:new).with(
48
- url,
49
- hash_including(ssl_verify_peer: false)
50
- ).and_call_original
51
- fetcher.get(url) do |tmp|
52
- expect(tmp).to be_a Tempfile
53
- expect(tmp.read).to eq 'world'
54
- expect(tmp.content_type).to eq 'text/plain'
55
- end
56
- end
57
-
58
- it 'can #get and fallback from streaming' do
59
- stub_request(:get, url).
60
- with(headers: fetcher.headers).
61
- to_return(
62
- { status: 501 },
63
- {
64
- status: 200,
65
- body: 'world',
66
- headers: { 'Content-Type' => 'text/plain' },
67
- }
68
- )
69
- fetcher.get(url) do |tmp|
70
- expect(tmp).to be_a Tempfile
71
- expect(tmp.read).to eq 'world'
72
- expect(tmp.content_type).to eq 'text/plain'
73
- end
74
- end
75
-
76
- it 'can #get and finally fail' do
77
- stub_request(:get, url).
78
- with(headers: fetcher.headers).
79
- to_return(status: 500)
80
- expect(STDERR).to receive(:puts).with(/cannot.*get.*#{url}/i)
81
- fetcher.get(url) do |tmp|
82
- expect(tmp).to be_a StringIO
83
- expect(tmp.read).to eq ''
84
- expect(tmp.content_type).to eq 'text/plain'
85
- end
86
- end
87
-
88
- it 'can redirect' do
89
- expect(fetcher.middlewares).to include Excon::Middleware::RedirectFollower
90
- end
91
-
92
- it 'can .read' do
93
- described_class.read(__FILE__) do |file|
94
- expect(file).to be_a File
95
- expect(file.read).to include 'can .read'
96
- expect(file.content_type).to eq 'application/x-ruby'
97
- end
98
- end
99
-
100
- it 'can .execute' do
101
- described_class.execute('echo -n hello world') do |file|
102
- expect(file).to be_a Tempfile
103
- expect(file.read).to eq 'hello world'
104
- expect(file.content_type).to eq 'text/plain'
105
- end
106
- end
107
-
108
- it 'can .execute and fail' do
109
- expect(IO).to receive(:popen).and_raise StandardError
110
- expect(STDERR).to receive(:puts).with(/cannot.*execute.*foobar/i)
111
- described_class.execute('foobar') do |file|
112
- expect(file).to be_a StringIO
113
- expect(file.read).to be_empty
114
- expect(file.content_type).to eq 'text/plain'
115
- end
116
- end
117
-
118
- describe '.normalize_url' do
119
- it 'can handle umlauts' do
120
- expect(described_class.normalize_url('https://foo.de/bär')).to eq(
121
- 'https://foo.de/b%C3%A4r'
122
- )
123
- end
124
-
125
- it 'can handle escaped umlauts' do
126
- expect(described_class.normalize_url('https://foo.de/b%C3%A4r')).to eq(
127
- 'https://foo.de/b%C3%A4r'
128
- )
129
- end
130
-
131
- it 'can remove #anchors' do
132
- expect(described_class.normalize_url('https://foo.de#bar')).to eq(
133
- 'https://foo.de'
134
- )
135
- end
136
- end
137
- end
@@ -1,17 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Ollama::Utils::FileArgument do
4
- it 'it can return content' do
5
- expect(described_class.get_file_argument('foo')).to eq 'foo'
6
- end
7
-
8
- it 'it can return content at path' do
9
- expect(described_class.get_file_argument(asset('prompt.txt'))).to include\
10
- 'test prompt'
11
- end
12
-
13
- it 'it can return default content' do
14
- expect(described_class.get_file_argument('', default: 'foo')).to eq 'foo'
15
- expect(described_class.get_file_argument(nil, default: 'foo')).to eq 'foo'
16
- end
17
- end
@@ -1,53 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Ollama::Utils::Tags do
4
- it 'can be instantiated' do
5
- expect(described_class.new).to be_a described_class
6
- end
7
-
8
- it 'can contain unique tags and is sorted' do
9
- tags = described_class.new(%w[ bar foo ])
10
- expect(tags.to_a).to eq %w[ bar foo ]
11
- end
12
-
13
- it 'can contain unique tags with leading # characters and is sorted' do
14
- tags = described_class.new(%w[ #bar ##foo ])
15
- expect(tags.to_a).to eq %w[ bar foo ]
16
- end
17
-
18
- it 'tags can be added to it' do
19
- tags = described_class.new([ 'foo' ])
20
- tags.add 'bar'
21
- expect(tags.to_a).to eq %w[ bar foo ]
22
- end
23
-
24
- it 'can increase in size' do
25
- tags = described_class.new
26
- expect { tags.add 'foo' }.to change { tags.size }.from(0).to(1)
27
- expect { tags.add 'bar' }.to change { tags.size }.from(1).to(2)
28
- end
29
-
30
- it 'can be cleared' do
31
- tags = described_class.new([ 'foo', 'bar' ])
32
- expect { tags.clear }.to change { tags.size }.from(2).to(0)
33
- end
34
-
35
- it 'tags can be empty' do
36
- tags = described_class.new([ 'foo' ])
37
- expect { tags.clear }.to change { tags.empty? }.from(false).to(true)
38
- end
39
-
40
- it 'can be output nicely' do
41
- expect(described_class.new(%w[ #foo bar ]).to_s).to eq '#bar #foo'
42
- end
43
-
44
- it 'can be output nicely with links to source' do
45
- tags = described_class.new([ 'foo' ], source: 'https://foo.example.com')
46
- tags.add 'bar', source: '/path/to/bar.html'
47
- expect(tags.to_a).to eq %w[ bar foo ]
48
- tags.all? { expect(_1).to be_a(Ollama::Utils::Tags::Tag) }
49
- expect(tags.to_s).to eq(
50
- "\e]8;;file:///path/to/bar.html\e\\#bar\e]8;;\e\\ \e]8;;https://foo.example.com\e\\#foo\e]8;;\e\\"
51
- )
52
- end
53
- end