ollama-ruby 0.12.1 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|