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.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/CHANGES.md +39 -0
- data/README.md +70 -144
- data/Rakefile +5 -17
- data/bin/ollama_cli +37 -6
- data/lib/ollama/client/command.rb +2 -2
- data/lib/ollama/dto.rb +4 -0
- data/lib/ollama/version.rb +1 -1
- data/lib/ollama.rb +0 -11
- data/ollama-ruby.gemspec +11 -22
- data/spec/ollama/message_spec.rb +9 -0
- metadata +25 -255
- data/bin/ollama_chat +0 -1248
- data/config/redis.conf +0 -5
- data/docker-compose.yml +0 -10
- data/lib/ollama/documents/cache/common.rb +0 -36
- data/lib/ollama/documents/cache/memory_cache.rb +0 -44
- data/lib/ollama/documents/cache/records.rb +0 -87
- data/lib/ollama/documents/cache/redis_backed_memory_cache.rb +0 -39
- data/lib/ollama/documents/cache/redis_cache.rb +0 -68
- data/lib/ollama/documents/cache/sqlite_cache.rb +0 -215
- data/lib/ollama/documents/splitters/character.rb +0 -72
- data/lib/ollama/documents/splitters/semantic.rb +0 -91
- data/lib/ollama/documents.rb +0 -184
- data/lib/ollama/utils/cache_fetcher.rb +0 -38
- data/lib/ollama/utils/chooser.rb +0 -52
- data/lib/ollama/utils/colorize_texts.rb +0 -65
- data/lib/ollama/utils/fetcher.rb +0 -175
- data/lib/ollama/utils/file_argument.rb +0 -34
- data/lib/ollama/utils/math.rb +0 -48
- data/lib/ollama/utils/tags.rb +0 -67
- data/spec/assets/embeddings.json +0 -1
- data/spec/assets/prompt.txt +0 -1
- data/spec/ollama/documents/cache/memory_cache_spec.rb +0 -97
- data/spec/ollama/documents/cache/redis_backed_memory_cache_spec.rb +0 -118
- data/spec/ollama/documents/cache/redis_cache_spec.rb +0 -121
- data/spec/ollama/documents/cache/sqlite_cache_spec.rb +0 -141
- data/spec/ollama/documents/splitters/character_spec.rb +0 -110
- data/spec/ollama/documents/splitters/semantic_spec.rb +0 -56
- data/spec/ollama/documents_spec.rb +0 -162
- data/spec/ollama/utils/cache_fetcher_spec.rb +0 -43
- data/spec/ollama/utils/colorize_texts_spec.rb +0 -13
- data/spec/ollama/utils/fetcher_spec.rb +0 -137
- data/spec/ollama/utils/file_argument_spec.rb +0 -17
- 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
|