ollama-ruby 0.0.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 (79) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +5 -0
  3. data/LICENSE +19 -0
  4. data/README.md +430 -0
  5. data/Rakefile +35 -0
  6. data/bin/ollama_chat +258 -0
  7. data/bin/ollama_console +20 -0
  8. data/lib/ollama/client/command.rb +25 -0
  9. data/lib/ollama/client/doc.rb +26 -0
  10. data/lib/ollama/client.rb +137 -0
  11. data/lib/ollama/commands/chat.rb +21 -0
  12. data/lib/ollama/commands/copy.rb +19 -0
  13. data/lib/ollama/commands/create.rb +20 -0
  14. data/lib/ollama/commands/delete.rb +19 -0
  15. data/lib/ollama/commands/embed.rb +21 -0
  16. data/lib/ollama/commands/embeddings.rb +20 -0
  17. data/lib/ollama/commands/generate.rb +21 -0
  18. data/lib/ollama/commands/ps.rb +19 -0
  19. data/lib/ollama/commands/pull.rb +19 -0
  20. data/lib/ollama/commands/push.rb +19 -0
  21. data/lib/ollama/commands/show.rb +20 -0
  22. data/lib/ollama/commands/tags.rb +19 -0
  23. data/lib/ollama/dto.rb +42 -0
  24. data/lib/ollama/errors.rb +15 -0
  25. data/lib/ollama/handlers/collector.rb +17 -0
  26. data/lib/ollama/handlers/concern.rb +31 -0
  27. data/lib/ollama/handlers/dump_json.rb +8 -0
  28. data/lib/ollama/handlers/dump_yaml.rb +8 -0
  29. data/lib/ollama/handlers/markdown.rb +22 -0
  30. data/lib/ollama/handlers/nop.rb +7 -0
  31. data/lib/ollama/handlers/print.rb +16 -0
  32. data/lib/ollama/handlers/progress.rb +36 -0
  33. data/lib/ollama/handlers/say.rb +19 -0
  34. data/lib/ollama/handlers/single.rb +17 -0
  35. data/lib/ollama/handlers.rb +13 -0
  36. data/lib/ollama/image.rb +31 -0
  37. data/lib/ollama/message.rb +9 -0
  38. data/lib/ollama/options.rb +68 -0
  39. data/lib/ollama/response.rb +5 -0
  40. data/lib/ollama/tool/function/parameters/property.rb +9 -0
  41. data/lib/ollama/tool/function/parameters.rb +10 -0
  42. data/lib/ollama/tool/function.rb +11 -0
  43. data/lib/ollama/tool.rb +9 -0
  44. data/lib/ollama/utils/ansi_markdown.rb +217 -0
  45. data/lib/ollama/utils/width.rb +22 -0
  46. data/lib/ollama/version.rb +8 -0
  47. data/lib/ollama.rb +43 -0
  48. data/ollama-ruby.gemspec +36 -0
  49. data/spec/assets/kitten.jpg +0 -0
  50. data/spec/ollama/client/doc_spec.rb +11 -0
  51. data/spec/ollama/client_spec.rb +144 -0
  52. data/spec/ollama/commands/chat_spec.rb +52 -0
  53. data/spec/ollama/commands/copy_spec.rb +28 -0
  54. data/spec/ollama/commands/create_spec.rb +37 -0
  55. data/spec/ollama/commands/delete_spec.rb +28 -0
  56. data/spec/ollama/commands/embed_spec.rb +52 -0
  57. data/spec/ollama/commands/embeddings_spec.rb +38 -0
  58. data/spec/ollama/commands/generate_spec.rb +29 -0
  59. data/spec/ollama/commands/ps_spec.rb +25 -0
  60. data/spec/ollama/commands/pull_spec.rb +28 -0
  61. data/spec/ollama/commands/push_spec.rb +28 -0
  62. data/spec/ollama/commands/show_spec.rb +28 -0
  63. data/spec/ollama/commands/tags_spec.rb +22 -0
  64. data/spec/ollama/handlers/collector_spec.rb +15 -0
  65. data/spec/ollama/handlers/dump_json_spec.rb +16 -0
  66. data/spec/ollama/handlers/dump_yaml_spec.rb +18 -0
  67. data/spec/ollama/handlers/markdown_spec.rb +46 -0
  68. data/spec/ollama/handlers/nop_spec.rb +15 -0
  69. data/spec/ollama/handlers/print_spec.rb +30 -0
  70. data/spec/ollama/handlers/progress_spec.rb +22 -0
  71. data/spec/ollama/handlers/say_spec.rb +30 -0
  72. data/spec/ollama/handlers/single_spec.rb +24 -0
  73. data/spec/ollama/image_spec.rb +23 -0
  74. data/spec/ollama/message_spec.rb +37 -0
  75. data/spec/ollama/options_spec.rb +25 -0
  76. data/spec/ollama/tool_spec.rb +78 -0
  77. data/spec/ollama/utils/ansi_markdown_spec.rb +15 -0
  78. data/spec/spec_helper.rb +16 -0
  79. metadata +321 -0
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Commands::Show do
4
+ it 'can be instantiated' do
5
+ show = described_class.new(name: 'llama3.1')
6
+ expect(show).to be_a described_class
7
+ end
8
+
9
+ it 'can be converted to JSON' do
10
+ show = described_class.new(name: 'llama3.1')
11
+ expect(show.as_json).to include(
12
+ name: 'llama3.1', stream: false
13
+ )
14
+ expect(show.to_json).to eq(
15
+ '{"json_class":"Ollama::Commands::Show","name":"llama3.1","stream":false}'
16
+ )
17
+ end
18
+
19
+ it 'can perform' do
20
+ show = described_class.new(name: 'llama3.1')
21
+ show.client = client = double('client')
22
+ expect(client).to receive(:request).with(
23
+ method: :post, path: '/api/show', handler: Ollama::Handlers::NOP ,stream: false,
24
+ body: '{"json_class":"Ollama::Commands::Show","name":"llama3.1","stream":false}'
25
+ )
26
+ show.perform(Ollama::Handlers::NOP)
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Commands::Tags do
4
+ let :tags do
5
+ described_class.new
6
+ end
7
+
8
+ it 'can be instantiated' do
9
+ expect(tags).to be_a described_class
10
+ end
11
+
12
+ it 'cannot be converted to JSON' do
13
+ expect(tags).not_to respond_to(:as_json)
14
+ end
15
+
16
+ it 'can perform' do
17
+ tags.client = client = double('client')
18
+ expect(client).to receive(:request).
19
+ with(method: :get, path: '/api/tags', stream: false, handler: Ollama::Handlers::NOP)
20
+ tags.perform(Ollama::Handlers::NOP)
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::Collector do
4
+ it 'has .call' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can collect responses in an array' do
10
+ collector = described_class.new(output:)
11
+ response = Ollama::Response[foo: 'testing']
12
+ collector.call(response)
13
+ expect(collector.result).to eq [ response ]
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::DumpJSON do
4
+ it 'has .call' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can print pretty JSON' do
10
+ output = double('output')
11
+ expect(output).to receive(:puts).with(%Q'{\n "foo": "testing"\n}')
12
+ print = described_class.new(output:)
13
+ response = Ollama::Response[foo: 'testing']
14
+ print.call(response)
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::DumpYAML do
4
+ it 'has .call' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can print YAML' do
10
+ output = double('output')
11
+ expect(output).to receive(:puts).with(
12
+ "--- !ruby/object:Ollama::Response\nfoo: testing\n"
13
+ )
14
+ print = described_class.new(output:)
15
+ response = Ollama::Response[foo: 'testing']
16
+ print.call(response)
17
+ end
18
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::Markdown do
4
+ it 'has .call' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ let :md do
10
+ <<~end
11
+ - **strong**
12
+ - *emphasized*
13
+ end
14
+ end
15
+
16
+ let :ansi do
17
+ <<~end
18
+ · \e[1mstrong\e[0m
19
+
20
+ · \e[3memphasized\e[0m
21
+
22
+ end
23
+ end
24
+
25
+ it 'can markdown response as markdown' do
26
+ output = double('output', :sync= => true)
27
+ expect(output).to receive(:print).with("\e[2J", "\e[1;1H", ansi)
28
+ expect(output).to receive(:puts)
29
+ markdown = described_class.new(output:)
30
+ response = double('response', response: md, done: false)
31
+ markdown.call(response)
32
+ response = double('response', response: nil, message: nil, done: true)
33
+ markdown.call(response)
34
+ end
35
+
36
+ it 'can markdown message content as markdown' do
37
+ output = double('output', :sync= => true)
38
+ expect(output).to receive(:print).with("\e[2J", "\e[1;1H", ansi)
39
+ expect(output).to receive(:puts)
40
+ markdown = described_class.new(output:)
41
+ response = double('response', response: nil, message: double(content: md), done: false)
42
+ markdown.call(response)
43
+ response = double('response', response: nil, message: nil, done: true)
44
+ markdown.call(response)
45
+ end
46
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::NOP do
4
+ it 'has .to_proc' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can do nothing at all' do
10
+ nop = described_class.new(output:)
11
+ response = Ollama::Response[foo: 'testing']
12
+ nop.call(response)
13
+ expect(nop.result).to be_nil
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::Print do
4
+ it 'has .to_proc' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can print response' do
10
+ output = double('output', :sync= => true)
11
+ expect(output).to receive(:print).with('testing')
12
+ expect(output).to receive(:puts)
13
+ print = described_class.new(output:)
14
+ response = double('response', response: 'testing', done: false)
15
+ print.call(response)
16
+ response = double('response', response: nil, message: nil, done: true)
17
+ print.call(response)
18
+ end
19
+
20
+ it 'can print message content' do
21
+ output = double('output', :sync= => true)
22
+ expect(output).to receive(:print).with('testing')
23
+ expect(output).to receive(:puts)
24
+ print = described_class.new(output:)
25
+ response = double('response', response: nil, message: double(content: 'testing'), done: false)
26
+ print.call(response)
27
+ response = double('response', response: nil, message: nil, done: true)
28
+ print.call(response)
29
+ end
30
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::Progress do
4
+ it 'has .to_proc' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can display progress' do
10
+ response = double('response', status: 'testing', completed: 23, total: 666)
11
+ expect(infobar.counter).to receive(:progress).with(by: 23).and_call_original
12
+ expect(infobar.display).to receive(:update).and_call_original
13
+ described_class.new.call(response)
14
+ end
15
+
16
+ it 'can display errors in progress' do
17
+ response = double('response', error: 'foo', status: nil, completed: nil, total: nil)
18
+ progress = described_class.new
19
+ expect(progress.output).to receive(:puts).with(/Error: .*foo/)
20
+ progress.call(response)
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::Say do
4
+ it 'has .to_proc' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can print response' do
10
+ output = double('output', :sync= => true)
11
+ expect(output).to receive(:print).with('testing')
12
+ expect(output).to receive(:close)
13
+ print = described_class.new(output:)
14
+ response = double('response', response: 'testing', done: false)
15
+ print.call(response)
16
+ response = double('response', response: nil, message: nil, done: true)
17
+ print.call(response)
18
+ end
19
+
20
+ it 'can print message content' do
21
+ output = double('output', :sync= => true)
22
+ expect(output).to receive(:print).with('testing')
23
+ expect(output).to receive(:close)
24
+ print = described_class.new(output:)
25
+ response = double('response', response: nil, message: double(content: 'testing'), done: false)
26
+ print.call(response)
27
+ response = double('response', response: nil, message: nil, done: true)
28
+ print.call(response)
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Handlers::Single do
4
+ it 'has .call' do
5
+ expect_any_instance_of(described_class).to receive(:call).with(:foo)
6
+ described_class.call(:foo)
7
+ end
8
+
9
+ it 'can collect responses in an array' do
10
+ single = described_class.new(output:)
11
+ response1 = Ollama::Response[foo: 'testing1']
12
+ response2 = Ollama::Response[foo: 'testing2']
13
+ single.call(response1)
14
+ single.call(response2)
15
+ expect(single.result).to eq [ response1, response2 ]
16
+ end
17
+
18
+ it 'can return only the single result' do
19
+ single = described_class.new(output:)
20
+ response = Ollama::Response[foo: 'testing']
21
+ single.call(response)
22
+ expect(single.result).to eq response
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Image do
4
+ let :image do
5
+ described_class.for_filename(asset('kitten.jpg'))
6
+ end
7
+
8
+ it 'can be instantiated' do
9
+ expect(image).to be_a described_class
10
+ end
11
+
12
+ it 'cannot be created via .new' do
13
+ expect {
14
+ described_class.new('nix')
15
+ }.to raise_error NoMethodError
16
+ end
17
+
18
+ it 'can be converted to base64 string' do
19
+ expect(image.to_s.size).to eq 134400
20
+ expect(image.to_s.sum).to eq 42460
21
+ expect(image.to_s[0, 40]).to eq '/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAA'
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Message do
4
+ let :image do
5
+ Ollama::Image.for_string("test")
6
+ end
7
+
8
+ let :message do
9
+ described_class.new(
10
+ role: 'user',
11
+ content: 'hello world',
12
+ images: image
13
+ )
14
+ end
15
+
16
+ it 'can be instantiated' do
17
+ expect(message).to be_a described_class
18
+ end
19
+
20
+ it 'can be converted to JSON' do
21
+ expect(message.as_json).to eq(
22
+ json_class: described_class.name,
23
+ role: 'user',
24
+ content: 'hello world',
25
+ images: [ image ],
26
+ )
27
+ expect(message.to_json).to eq(
28
+ '{"json_class":"Ollama::Message","role":"user","content":"hello world","images":["dGVzdA==\n"]}'
29
+ )
30
+ end
31
+
32
+ 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"]}
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Options do
4
+ let :options do
5
+ described_class.new(
6
+ penalize_newline: true,
7
+ num_ctx: 8192,
8
+ temperature: 0.7,
9
+ )
10
+ end
11
+
12
+ it 'can be instantiated' do
13
+ expect(options).to be_a described_class
14
+ end
15
+
16
+ it 'throws error for invalid types' do
17
+ expect { described_class.new(temperature: Class.new) }.
18
+ to raise_error(TypeError)
19
+ end
20
+
21
+ it 'throws error for invalid boolean values' do
22
+ expect { described_class.new(penalize_newline: :tertium) }.
23
+ to raise_error(TypeError)
24
+ end
25
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Tool do
4
+ let :location do
5
+ Ollama::Tool::Function::Parameters::Property.new(
6
+ type: 'string',
7
+ description: 'The location to get the weather for, e.g. Berlin, Berlin',
8
+ )
9
+ end
10
+
11
+ let :format do
12
+ Ollama::Tool::Function::Parameters::Property.new(
13
+ type: 'string',
14
+ description: "The format to return the weather in, e.g. 'celsius' or 'fahrenheit'",
15
+ enum: %w[ celsius fahrenheit ]
16
+ )
17
+ end
18
+
19
+ let :parameters do
20
+ Ollama::Tool::Function::Parameters.new(
21
+ type: 'object',
22
+ properties: { location:, format: },
23
+ required: %w[ location format ],
24
+ )
25
+ end
26
+
27
+ let :function do
28
+ Ollama::Tool::Function.new(
29
+ name: 'get_current_weather',
30
+ description: 'Get the current weather for a location',
31
+ parameters:,
32
+ )
33
+ end
34
+
35
+ let :tool do
36
+ described_class.new(
37
+ type: 'function',
38
+ function:,
39
+ )
40
+ end
41
+
42
+ it 'can be instantiated' do
43
+ expect(tool).to be_a described_class
44
+ end
45
+
46
+ it 'cannot be converted to JSON' do
47
+ expect(tool.as_json).to eq(
48
+ json_class: described_class.name,
49
+ type: 'function',
50
+ function: {
51
+ json_class: "Ollama::Tool::Function",
52
+ name: 'get_current_weather',
53
+ description: "Get the current weather for a location",
54
+ parameters: {
55
+ json_class: "Ollama::Tool::Function::Parameters",
56
+ type: "object",
57
+ properties: {
58
+ location: {
59
+ json_class: "Ollama::Tool::Function::Parameters::Property",
60
+ type: "string",
61
+ description: "The location to get the weather for, e.g. Berlin, Berlin"
62
+ },
63
+ format: {
64
+ json_class: "Ollama::Tool::Function::Parameters::Property",
65
+ type: "string",
66
+ description: "The format to return the weather in, e.g. 'celsius' or 'fahrenheit'",
67
+ enum: ["celsius", "fahrenheit"]
68
+ }
69
+ },
70
+ required: ["location", "format"]
71
+ },
72
+ }
73
+ )
74
+ 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"]}}}}
76
+ )
77
+ end
78
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Ollama::Utils::ANSIMarkdown do
4
+ let :source do
5
+ File.read(Pathname.new(__dir__) + '..' + '..' + '..' + 'README.md')
6
+ end
7
+
8
+ it 'can parse' do
9
+ File.open('tmp/README.ansi', ?w) do |output|
10
+ ansi = described_class.parse(source)
11
+ expect(ansi).to match("This is the end.")
12
+ output.puts ansi
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ if ENV['START_SIMPLECOV'].to_i == 1
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ add_filter "#{File.basename(File.dirname(__FILE__))}/"
5
+ end
6
+ end
7
+ require 'rspec'
8
+ begin
9
+ require 'debug'
10
+ rescue LoadError
11
+ end
12
+ require 'ollama'
13
+
14
+ def asset(name)
15
+ File.join(__dir__, 'assets', name)
16
+ end