ollama-ruby 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +1 -0
  3. data/CHANGES.md +78 -0
  4. data/README.md +62 -23
  5. data/Rakefile +16 -4
  6. data/bin/ollama_chat +470 -90
  7. data/bin/ollama_console +3 -3
  8. data/bin/ollama_update +17 -0
  9. data/config/redis.conf +5 -0
  10. data/docker-compose.yml +11 -0
  11. data/lib/ollama/client.rb +7 -2
  12. data/lib/ollama/documents/memory_cache.rb +44 -0
  13. data/lib/ollama/documents/redis_cache.rb +57 -0
  14. data/lib/ollama/documents/splitters/character.rb +70 -0
  15. data/lib/ollama/documents/splitters/semantic.rb +90 -0
  16. data/lib/ollama/documents.rb +172 -0
  17. data/lib/ollama/dto.rb +4 -7
  18. data/lib/ollama/handlers/progress.rb +18 -5
  19. data/lib/ollama/image.rb +16 -7
  20. data/lib/ollama/options.rb +4 -0
  21. data/lib/ollama/utils/chooser.rb +30 -0
  22. data/lib/ollama/utils/colorize_texts.rb +42 -0
  23. data/lib/ollama/utils/fetcher.rb +105 -0
  24. data/lib/ollama/utils/math.rb +48 -0
  25. data/lib/ollama/utils/tags.rb +7 -0
  26. data/lib/ollama/utils/width.rb +1 -1
  27. data/lib/ollama/version.rb +1 -1
  28. data/lib/ollama.rb +12 -5
  29. data/ollama-ruby.gemspec +19 -9
  30. data/spec/assets/embeddings.json +1 -0
  31. data/spec/ollama/client_spec.rb +2 -2
  32. data/spec/ollama/commands/chat_spec.rb +2 -2
  33. data/spec/ollama/commands/copy_spec.rb +2 -2
  34. data/spec/ollama/commands/create_spec.rb +2 -2
  35. data/spec/ollama/commands/delete_spec.rb +2 -2
  36. data/spec/ollama/commands/embed_spec.rb +3 -3
  37. data/spec/ollama/commands/embeddings_spec.rb +2 -2
  38. data/spec/ollama/commands/generate_spec.rb +2 -2
  39. data/spec/ollama/commands/pull_spec.rb +2 -2
  40. data/spec/ollama/commands/push_spec.rb +2 -2
  41. data/spec/ollama/commands/show_spec.rb +2 -2
  42. data/spec/ollama/documents/memory_cache_spec.rb +63 -0
  43. data/spec/ollama/documents/redis_cache_spec.rb +78 -0
  44. data/spec/ollama/documents/splitters/character_spec.rb +96 -0
  45. data/spec/ollama/documents/splitters/semantic_spec.rb +56 -0
  46. data/spec/ollama/documents_spec.rb +119 -0
  47. data/spec/ollama/handlers/progress_spec.rb +2 -2
  48. data/spec/ollama/image_spec.rb +4 -0
  49. data/spec/ollama/message_spec.rb +3 -4
  50. data/spec/ollama/options_spec.rb +18 -0
  51. data/spec/ollama/tool_spec.rb +1 -6
  52. data/spec/ollama/utils/fetcher_spec.rb +74 -0
  53. data/spec/ollama/utils/tags_spec.rb +24 -0
  54. data/spec/spec_helper.rb +8 -0
  55. data/tmp/.keep +0 -0
  56. metadata +187 -5
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Documents::MemoryCache do
4
+ let :memory_cache do
5
+ described_class.new prefix: 'test-'
6
+ end
7
+
8
+ it 'can be instantiated' do
9
+ expect(memory_cache).to be_a described_class
10
+ end
11
+
12
+ it 'can get/set a key' do
13
+ key, value = 'foo', { test: true }
14
+ expect {
15
+ memory_cache[key] = value
16
+ }.to change {
17
+ memory_cache[key]
18
+ }.from(nil).to(value)
19
+ end
20
+
21
+ it 'can determine if key exists' do
22
+ key, value = 'foo', { test: true }
23
+ expect {
24
+ memory_cache[key] = value
25
+ }.to change {
26
+ memory_cache.key?(key)
27
+ }.from(false).to(true)
28
+ end
29
+
30
+ it 'can delete' do
31
+ key, value = 'foo', { test: true }
32
+ memory_cache[key] = value
33
+ expect {
34
+ memory_cache.delete(key)
35
+ }.to change {
36
+ memory_cache.key?(key)
37
+ }.from(true).to(false)
38
+ end
39
+
40
+ it 'returns size' do
41
+ key, value = 'foo', { test: true }
42
+ expect {
43
+ memory_cache[key] = value
44
+ }.to change {
45
+ memory_cache.size
46
+ }.from(0).to(1)
47
+ end
48
+
49
+ it 'can clear' do
50
+ key, value = 'foo', { test: true }
51
+ memory_cache[key] = value
52
+ expect {
53
+ expect(memory_cache.clear).to eq memory_cache
54
+ }.to change {
55
+ memory_cache.size
56
+ }.from(1).to(0)
57
+ end
58
+
59
+ it 'can iterate over keys under a prefix' do
60
+ memory_cache['foo'] = 'bar'
61
+ expect(memory_cache.to_a).to eq [ %w[ test-foo bar ] ]
62
+ end
63
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Documents::RedisCache do
4
+ it 'can be instantiated' do
5
+ redis_cache = described_class.new prefix: 'test-', url: 'something'
6
+ expect(redis_cache).to be_a described_class
7
+ end
8
+
9
+ it 'raises ArgumentError if url is missing' do
10
+ expect {
11
+ described_class.new prefix: 'test-', url: nil
12
+ }.to raise_error ArgumentError
13
+ end
14
+
15
+ context 'test redis interactions' do
16
+ let :redis_cache do
17
+ described_class.new prefix: 'test-', url: 'something'
18
+ end
19
+
20
+ let :redis do
21
+ double('Redis')
22
+ end
23
+
24
+ before do
25
+ allow_any_instance_of(described_class).to receive(:redis).and_return(redis)
26
+ end
27
+
28
+ it 'has Redis client' do
29
+ expect(redis_cache.redis).to eq redis
30
+ end
31
+
32
+ it 'can get a key' do
33
+ key = 'foo'
34
+ expect(redis).to receive(:get).with('test-' + key).and_return 666
35
+ redis_cache[key]
36
+ end
37
+
38
+ it 'can set a value for a key' do
39
+ key, value = 'foo', { test: true }
40
+ expect(redis).to receive(:set).with('test-' + key, JSON(value))
41
+ redis_cache[key] = value
42
+ end
43
+
44
+ it 'can determine if key exists' do
45
+ key = 'foo'
46
+ expect(redis).to receive(:exists?).with('test-' + key).and_return(false, true)
47
+ expect(redis_cache.key?('foo')).to eq false
48
+ expect(redis_cache.key?('foo')).to eq true
49
+ end
50
+
51
+ it 'can delete' do
52
+ key = 'foo'
53
+ expect(redis).to receive(:del).with('test-' + key)
54
+ redis_cache.delete(key)
55
+ end
56
+
57
+ it 'returns size' do
58
+ allow(redis).to receive(:scan_each).with(match: 'test-*').
59
+ and_yield('test-foo').
60
+ and_yield('test-bar').
61
+ and_yield('test-baz')
62
+ expect(redis_cache.size).to eq 3
63
+ end
64
+
65
+ it 'can clear' do
66
+ expect(redis).to receive(:scan_each).with(match: 'test-*').and_yield(
67
+ 'test-foo'
68
+ )
69
+ expect(redis).to receive(:del).with('test-foo')
70
+ expect(redis_cache.clear).to eq redis_cache
71
+ end
72
+
73
+ it 'can iterate over keys under a prefix' do
74
+ expect(redis).to receive(:scan_each).with(match: 'test-*')
75
+ redis_cache.to_a
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Documents::Splitters::Character do
4
+ let :splitter do
5
+ described_class.new chunk_size: 23
6
+ end
7
+
8
+ it 'can be instantiated' do
9
+ expect(splitter).to be_a described_class
10
+ end
11
+
12
+ it 'can split' do
13
+ text = [ "A" * 10 ] * 10 * "\n\n"
14
+ result = splitter.split(text)
15
+ expect(result.count).to eq 5
16
+ expect(result.to_a.join('')).to eq ?A * 100
17
+ end
18
+
19
+ it 'can split including separator' do
20
+ splitter = described_class.new chunk_size: 25, include_separator: true
21
+ text = [ "A" * 10 ] * 10 * "\n\n"
22
+ result = splitter.split(text)
23
+ expect(result.count).to eq 5
24
+ expect(result.to_a.join('')).to eq text
25
+ end
26
+
27
+ it 'cannot split' do
28
+ text = [ "A" * 10 ] * 10 * "\n"
29
+ result = splitter.split(text)
30
+ expect(result.count).to eq 1
31
+ expect(result.to_a.join('').count(?A)).to eq text.count(?A)
32
+ end
33
+
34
+ it 'cannot split2' do
35
+ text = "A" * 25
36
+ result = splitter.split(text)
37
+ expect(result.count).to eq 1
38
+ expect(result.to_a.join('')).to eq ?A * 25
39
+ end
40
+
41
+ it 'can split sentences' do
42
+ text = "foo.foo. bar!bar! baz?baz? quux.\nquux."
43
+ splitter = described_class.new(separator: /[.!?]\s*(?:\b|\z)/, chunk_size: 2)
44
+ result = splitter.split(text)
45
+ expect(result.to_a).to eq %w[ foo foo bar bar baz baz quux quux ]
46
+ end
47
+ end
48
+
49
+ RSpec.describe Ollama::Documents::Splitters::RecursiveCharacter do
50
+ let :splitter do
51
+ described_class.new chunk_size: 23
52
+ end
53
+
54
+ it 'can be instantiated' do
55
+ expect(splitter).to be_a described_class
56
+ end
57
+
58
+ it 'can split' do
59
+ text = [ "A" * 10 ] * 10 * "\n\n"
60
+ result = splitter.split(text)
61
+ expect(result.count).to eq 5
62
+ expect(result.to_a.join('')).to eq ?A * 100
63
+ end
64
+
65
+ it 'cannot split' do
66
+ splitter = described_class.new chunk_size: 23, include_separator: true,
67
+ separators: described_class::DEFAULT_SEPARATORS[0..-2]
68
+ text = "A" * 25
69
+ result = splitter.split(text)
70
+ expect(result.count).to eq 1
71
+ expect(result.to_a.join('')).to eq ?A * 25
72
+ end
73
+
74
+ it 'can split including separator' do
75
+ splitter = described_class.new chunk_size: 25, include_separator: true
76
+ text = [ "A" * 10 ] * 10 * "\n\n"
77
+ result = splitter.split(text)
78
+ expect(result.count).to eq 5
79
+ expect(result.to_a.join('')).to eq text
80
+ end
81
+
82
+ it 'can split single newline as well' do
83
+ text = [ "A" * 10 ] * 10 * "\n"
84
+ result = splitter.split(text)
85
+ expect(result.count).to eq 5
86
+ expect(result.to_a.join('')).to eq ?A * 100
87
+ end
88
+
89
+ it 'can split single newline as well including separator' do
90
+ splitter = described_class.new chunk_size: 25, include_separator: true
91
+ text = [ "A" * 10 ] * 10 * "\n"
92
+ result = splitter.split(text)
93
+ expect(result.count).to eq 5
94
+ expect(result.to_a.join('')).to eq text
95
+ end
96
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Documents::Splitters::Semantic do
4
+ let :ollama do
5
+ double('Ollama::Client')
6
+ end
7
+
8
+ let :splitter do
9
+ described_class.new ollama:, model: 'mxbai-embed-large'
10
+ end
11
+
12
+ let :embeddings do
13
+ JSON(File.read(asset('embeddings.json')))
14
+ end
15
+
16
+ it 'can be instantiated' do
17
+ expect(splitter).to be_a described_class
18
+ end
19
+
20
+ before do
21
+ allow(ollama).to receive(:embed).and_return(double(embeddings:))
22
+ end
23
+
24
+ it 'can split with breakpoint :percentile' do
25
+ text = ([ "A" * 10 ] * 3 + [ "B" * 10 ] * 3 + [ "A" * 10 ] * 3) * ". "
26
+ result = splitter.split(text, breakpoint: :percentile, percentile: 75)
27
+ expect(result.count).to eq 3
28
+ expect(result.to_a.join('').count(?A)).to eq text.count(?A)
29
+ expect(result.to_a.join('').count(?B)).to eq text.count(?B)
30
+ end
31
+
32
+ it 'can split with breakpoint :percentile' do
33
+ described_class.new ollama:, model: 'mxbai-embed-large', chunk_size: 50
34
+ text = ([ "A" * 10 ] * 6 + [ "B" * 10 ] * 3 + [ "A" * 10 ] * 3) * ". "
35
+ result = splitter.split(text, breakpoint: :percentile, percentile: 75)
36
+ expect(result.count).to eq 4
37
+ expect(result.to_a.join('').count(?A)).to eq text.count(?A)
38
+ expect(result.to_a.join('').count(?B)).to eq text.count(?B)
39
+ end
40
+
41
+ it 'can split with breakpoint :standard_deviation' do
42
+ text = ([ "A" * 10 ] * 3 + [ "B" * 10 ] * 3 + [ "A" * 10 ] * 3) * ". "
43
+ result = splitter.split(text, breakpoint: :standard_deviation, percentage: 100)
44
+ expect(result.count).to eq 3
45
+ expect(result.to_a.join('').count(?A)).to eq text.count(?A)
46
+ expect(result.to_a.join('').count(?B)).to eq text.count(?B)
47
+ end
48
+
49
+ it 'can split with breakpoint :interquartile' do
50
+ text = ([ "A" * 10 ] * 3 + [ "B" * 10 ] * 3 + [ "A" * 10 ] * 3) * ". "
51
+ result = splitter.split(text, breakpoint: :interquartile, percentage: 75)
52
+ expect(result.count).to eq 3
53
+ expect(result.to_a.join('').count(?A)).to eq text.count(?A)
54
+ expect(result.to_a.join('').count(?B)).to eq text.count(?B)
55
+ end
56
+ end
@@ -0,0 +1,119 @@
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
+ allow(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
+ allow(ollama).to receive(:embed).
61
+ with(model:, input: [ 'foo' ], options: nil).
62
+ and_return(double(embeddings: [ [ 0.1 ] ]))
63
+ expect(documents.add('foo', tags: %i[ 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: %i[ 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: %i[ 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
+ context 'it uses cache' do
80
+ before do
81
+ allow(ollama).to receive(:embed).
82
+ with(model:, input: %w[ foo ], options: nil).
83
+ and_return(double(embeddings: [ [ 0.1 ] ]))
84
+ end
85
+
86
+ it 'can delete texts' do
87
+ expect(documents << 'foo').to eq documents
88
+ expect {
89
+ documents.delete('foo')
90
+ }.to change { documents.exist?('foo') }.from(true).to(false)
91
+ end
92
+
93
+ it 'tracks size' do
94
+ expect {
95
+ expect(documents << 'foo').to eq documents
96
+ }.to change { documents.size }.from(0).to(1)
97
+ end
98
+
99
+ it 'can clear texts' do
100
+ expect(documents << 'foo').to eq documents
101
+ expect {
102
+ documents.clear
103
+ }.to change { documents.size }.from(1).to(0)
104
+ end
105
+
106
+ it 'returns collections' do
107
+ expect(documents.collections).to eq [ :default ]
108
+ end
109
+
110
+ it 'can change collection' do
111
+ expect(documents.instance_eval { @cache }).to receive(:prefix=).
112
+ with(/#@collection/).and_call_original
113
+ expect { documents.collection = :new_collection }.
114
+ to change { documents.collection }.
115
+ from(:default).
116
+ to(:new_collection)
117
+ end
118
+ end
119
+ end
@@ -7,7 +7,7 @@ RSpec.describe Ollama::Handlers::Progress do
7
7
  end
8
8
 
9
9
  it 'can display progress' do
10
- response = double('response', status: 'testing', completed: 23, total: 666)
10
+ response = double('response', status: 'testing', completed: 23, total: 666, error: nil)
11
11
  expect(infobar.counter).to receive(:progress).with(by: 23).and_call_original
12
12
  expect(infobar.display).to receive(:update).and_call_original
13
13
  described_class.new.call(response)
@@ -16,7 +16,7 @@ RSpec.describe Ollama::Handlers::Progress do
16
16
  it 'can display errors in progress' do
17
17
  response = double('response', error: 'foo', status: nil, completed: nil, total: nil)
18
18
  progress = described_class.new
19
- expect(progress.output).to receive(:puts).with(/Error: .*foo/)
19
+ expect(infobar).to receive(:puts).with(/Error: .*foo/)
20
20
  progress.call(response)
21
21
  end
22
22
  end
@@ -20,4 +20,8 @@ RSpec.describe Ollama::Image do
20
20
  expect(image.to_s.sum).to eq 42460
21
21
  expect(image.to_s[0, 40]).to eq '/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAA'
22
22
  end
23
+
24
+ it 'tracks path of file' do
25
+ expect(image.path).to eq asset('kitten.jpg')
26
+ end
23
27
  end
@@ -19,19 +19,18 @@ RSpec.describe Ollama::Message do
19
19
 
20
20
  it 'can be converted to JSON' do
21
21
  expect(message.as_json).to eq(
22
- json_class: described_class.name,
23
22
  role: 'user',
24
23
  content: 'hello world',
25
24
  images: [ image ],
26
25
  )
27
26
  expect(message.to_json).to eq(
28
- '{"json_class":"Ollama::Message","role":"user","content":"hello world","images":["dGVzdA==\n"]}'
27
+ '{"role":"user","content":"hello world","images":["dGVzdA==\n"]}'
29
28
  )
30
29
  end
31
30
 
32
31
  it 'can be restored from JSON' do
33
- expect(JSON(<<~'end', create_additions: true)).to be_a described_class
34
- {"json_class":"Ollama::Message","role":"user","content":"hello world","images":["dGVzdA==\n"]}
32
+ expect(described_class.from_hash(JSON(<<~'end'))).to be_a described_class
33
+ {"role":"user","content":"hello world","images":["dGVzdA==\n"]}
35
34
  end
36
35
  end
37
36
  end
@@ -13,6 +13,24 @@ RSpec.describe Ollama::Options do
13
13
  expect(options).to be_a described_class
14
14
  end
15
15
 
16
+ it 'can be used to cast hashes' do
17
+ expect(described_class[{
18
+ penalize_newline: true,
19
+ num_ctx: 8192,
20
+ temperature: 0.7,
21
+ }]).to be_a described_class
22
+ end
23
+
24
+ it 'raises errors when casting goes all wrong' do
25
+ expect {
26
+ described_class[{
27
+ penalize_newline: :tertium,
28
+ num_ctx: 8192,
29
+ temperature: 0.7,
30
+ }]
31
+ }.to raise_error(TypeError)
32
+ end
33
+
16
34
  it 'throws error for invalid types' do
17
35
  expect { described_class.new(temperature: Class.new) }.
18
36
  to raise_error(TypeError)
@@ -45,23 +45,18 @@ RSpec.describe Ollama::Tool do
45
45
 
46
46
  it 'cannot be converted to JSON' do
47
47
  expect(tool.as_json).to eq(
48
- json_class: described_class.name,
49
48
  type: 'function',
50
49
  function: {
51
- json_class: "Ollama::Tool::Function",
52
50
  name: 'get_current_weather',
53
51
  description: "Get the current weather for a location",
54
52
  parameters: {
55
- json_class: "Ollama::Tool::Function::Parameters",
56
53
  type: "object",
57
54
  properties: {
58
55
  location: {
59
- json_class: "Ollama::Tool::Function::Parameters::Property",
60
56
  type: "string",
61
57
  description: "The location to get the weather for, e.g. Berlin, Berlin"
62
58
  },
63
59
  format: {
64
- json_class: "Ollama::Tool::Function::Parameters::Property",
65
60
  type: "string",
66
61
  description: "The format to return the weather in, e.g. 'celsius' or 'fahrenheit'",
67
62
  enum: ["celsius", "fahrenheit"]
@@ -72,7 +67,7 @@ RSpec.describe Ollama::Tool do
72
67
  }
73
68
  )
74
69
  expect(tool.to_json).to eq(
75
- %{{"json_class":"Ollama::Tool","type":"function","function":{"json_class":"Ollama::Tool::Function","name":"get_current_weather","description":"Get the current weather for a location","parameters":{"json_class":"Ollama::Tool::Function::Parameters","type":"object","properties":{"location":{"json_class":"Ollama::Tool::Function::Parameters::Property","type":"string","description":"The location to get the weather for, e.g. Berlin, Berlin"},"format":{"json_class":"Ollama::Tool::Function::Parameters::Property","type":"string","description":"The format to return the weather in, e.g. 'celsius' or 'fahrenheit'","enum":["celsius","fahrenheit"]}},"required":["location","format"]}}}}
70
+ %{{"type":"function","function":{"name":"get_current_weather","description":"Get the current weather for a location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The location to get the weather for, e.g. Berlin, Berlin"},"format":{"type":"string","description":"The format to return the weather in, e.g. 'celsius' or 'fahrenheit'","enum":["celsius","fahrenheit"]}},"required":["location","format"]}}}}
76
71
  )
77
72
  end
78
73
  end
@@ -0,0 +1,74 @@
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
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, 'https://www.example.com/hello').
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 and fallback from streaming' do
37
+ stub_request(:get, 'https://www.example.com/hello').
38
+ with(headers: fetcher.headers).
39
+ to_return(
40
+ { status: 501 },
41
+ {
42
+ status: 200,
43
+ body: 'world',
44
+ headers: { 'Content-Type' => 'text/plain' },
45
+ }
46
+ )
47
+ fetcher.get(url) do |tmp|
48
+ expect(tmp).to be_a Tempfile
49
+ expect(tmp.read).to eq 'world'
50
+ expect(tmp.content_type).to eq 'text/plain'
51
+ end
52
+ end
53
+
54
+ it 'can #get and finally fail' do
55
+ stub_request(:get, 'https://www.example.com/hello').
56
+ with(headers: fetcher.headers).
57
+ to_return(status: 500)
58
+ fetcher.get(url) do |tmp|
59
+ expect(tmp).to be_nil
60
+ end
61
+ end
62
+
63
+ it 'can redirect' do
64
+ expect(fetcher.middlewares).to include Excon::Middleware::RedirectFollower
65
+ end
66
+
67
+ it 'can .read' do
68
+ described_class.read(__FILE__) do |file|
69
+ expect(file).to be_a File
70
+ expect(file.read).to include 'can .read'
71
+ expect(file.content_type).to eq 'application/x-ruby'
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,24 @@
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([ 'bar', 'foo'])
10
+ expect(tags.to_a).to eq %w[ bar foo ]
11
+ end
12
+
13
+ it 'tags can be added to it' do
14
+ tags = described_class.new([ 'foo' ])
15
+ tags.add 'bar'
16
+ expect(tags.to_a).to eq %w[ bar foo ]
17
+ tags.merge [ 'baz', 'baz2' ]
18
+ expect(tags.to_a).to eq %w[ bar baz baz2 foo ]
19
+ end
20
+
21
+ it 'can be output nicely' do
22
+ expect(described_class.new(%w[foo bar]).to_s).to eq '#bar #foo'
23
+ end
24
+ end
data/spec/spec_helper.rb CHANGED
@@ -9,8 +9,16 @@ begin
9
9
  require 'debug'
10
10
  rescue LoadError
11
11
  end
12
+ require 'webmock/rspec'
13
+ WebMock.disable_net_connect!
12
14
  require 'ollama'
13
15
 
14
16
  def asset(name)
15
17
  File.join(__dir__, 'assets', name)
16
18
  end
19
+
20
+ RSpec.configure do |config|
21
+ config.before(:suite) do
22
+ infobar.show = nil
23
+ end
24
+ end
data/tmp/.keep ADDED
File without changes